摘要:的作用是為其他線程的運(yùn)行提供服務(wù),比如說線程。在某些平臺上,指定一個(gè)較高的參數(shù)值可能使線程在拋出之前達(dá)到較大的遞歸深度。參數(shù)的值與最大遞歸深度和并發(fā)程度之間的關(guān)系細(xì)節(jié)與平臺有關(guān)。
守護(hù)線程今天研究了下Java線程基礎(chǔ)知識,發(fā)現(xiàn)以前太多知識知識略略帶過了,比較說Java的線程機(jī)制,在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護(hù)線程),以及構(gòu)造器中的stackSize.....
估計(jì)學(xué)過Unix開發(fā)但是沒有細(xì)致學(xué)習(xí)Java的同學(xué)們會疑惑了,操作系統(tǒng)里面是沒有所謂的守護(hù)線程的概念,只有守護(hù)進(jìn)程一說,但是Java語言機(jī)制是構(gòu)建在JVM的基礎(chǔ)之上的,意思是Java平臺把操作系統(tǒng)的底層給屏蔽起來,所以它可以在它自己的虛擬的平臺里面構(gòu)造出對自己有利的機(jī)制,而語言或者說平臺的設(shè)計(jì)者多多少少是受到Unix思想的影響,而守護(hù)線程機(jī)制又是對JVM這樣的平臺湊合,于是守護(hù)線程應(yīng)運(yùn)而生。
Daemon的作用是為其他線程的運(yùn)行提供服務(wù),比如說GC線程。其實(shí)User Thread線程和Daemon Thread守護(hù)線程本質(zhì)上來說去沒啥區(qū)別的,唯一的區(qū)別之處就在虛擬機(jī)的離開:如果User Thread全部撤離,那么Daemon Thread也就沒啥線程好服務(wù)的了,所以虛擬機(jī)也就退出了。
守護(hù)線程并非虛擬機(jī)內(nèi)部可以提供,用戶也可以自行的設(shè)定守護(hù)線程,方法:setDaemon(boolean on) 但是有幾點(diǎn)需要注意:
thread.setDaemon(true)必須在thread.start()之前設(shè)置,否則會跑出一個(gè)IllegalThreadStateException異常,因?yàn)槟悴荒馨颜谶\(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程。(備注:這點(diǎn)與守護(hù)進(jìn)程有著明顯的區(qū)別,守護(hù)進(jìn)程是創(chuàng)建后,讓進(jìn)程擺脫原會話的控制+讓進(jìn)程擺脫原進(jìn)程組的控制+讓進(jìn)程擺脫原控制終端的控制;所以說寄托于虛擬機(jī)的語言機(jī)制跟系統(tǒng)級語言有著本質(zhì)上面的區(qū)別)
public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) {//如果處于運(yùn)行狀態(tài),拋出異常 throw new IllegalThreadStateException(); } daemon = on; }
在Daemon線程中產(chǎn)生的新線程也是Daemon的。(這一點(diǎn)又是有著本質(zhì)的區(qū)別了:守護(hù)進(jìn)程fork()出來的子進(jìn)程不再是守護(hù)進(jìn)程,盡管它把父進(jìn)程的進(jìn)程相關(guān)信息復(fù)制過去了,但是子進(jìn)程的進(jìn)程的父進(jìn)程不是init進(jìn)程,所謂的守護(hù)進(jìn)程本質(zhì)上說就是父進(jìn)程掛掉,init收養(yǎng))
不是所有的應(yīng)用都可以分配給Daemon線程來進(jìn)行服務(wù),比如讀寫操作或者計(jì)算邏輯。因?yàn)樵?b>Daemon Thread還沒來的及進(jìn)行操作時(shí),虛擬機(jī)可能已經(jīng)退出了。
Thread thread = new Thread(() -> { Thread innerThread = new Thread(() -> { try { for (int i = 1; i < 10; i++) { Thread.sleep(1_000); System.out.println("守護(hù)線程 " + i); } } catch (InterruptedException e) { e.printStackTrace(); } }); innerThread.setDaemon(true); innerThread.start(); try { for (int i = 1; i < 6; i++) { Thread.sleep(1_000); System.out.println("常規(guī)線程 " + i); } } catch (InterruptedException e) { e.printStackTrace(); } }); thread.start();日志分析
常規(guī)線程 1 守護(hù)線程 1 守護(hù)線程 2 常規(guī)線程 2 守護(hù)線程 3 常規(guī)線程 3 守護(hù)線程 4 常規(guī)線程 4 守護(hù)線程 5 常規(guī)線程 5
從上面的日志中可以看到,如果將innerThread設(shè)置成守護(hù)模式,那么待當(dāng)前主線程完成處理退出后,守護(hù)線程也會隨著銷毀
JRE判斷程序是否執(zhí)行結(jié)束的標(biāo)準(zhǔn)是所有的前臺執(zhí)線程行完畢了,而不管后臺線程的狀態(tài),因此,在使用后臺線程候一定要注意這個(gè)問題。
那么daemon Thread實(shí)際應(yīng)用在那里呢?舉個(gè)例子,Web服務(wù)器中的Servlet,容器啟動時(shí)后臺初始化一個(gè)服務(wù)線程,即調(diào)度線程,負(fù)責(zé)處理http請求,然后每個(gè)請求過來調(diào)度線程從線程池中取出一個(gè)工作者線程來處理該請求,從而實(shí)現(xiàn)并發(fā)控制的目的。
線程函數(shù)在學(xué)習(xí)Thread的時(shí)候,常見寫法就是Runnable和ThreadName,所以這里重點(diǎn)講解ThreadGroup以及stackSize的作用
public Thread(); public Thread(Runnable target); public Thread(String name); public Thread(Runnable target, String name); public Thread(ThreadGroup group, Runnable target); public Thread(ThreadGroup group, String name); public Thread(ThreadGroup group, Runnable target, String name); public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
第一種是實(shí)例化一個(gè)無參構(gòu)造函數(shù),ThreadName與GroupName為系統(tǒng)默認(rèn)
第二種是實(shí)現(xiàn)了Runnable接口的類的實(shí)例,Thread類也實(shí)現(xiàn)了Runnable接口,因此,從Thread類繼承的類的實(shí)例也可以作為target傳入這個(gè)構(gòu)造方法。
第三種是可以自定義ThreadName
第五種是可以指定該線程屬于哪個(gè)ThreadGroup(線程組)
第八種是可以指定堆棧大小(比如壓棧大小),這個(gè)值一般是CPU頁面的整數(shù)倍,如x86的頁面大小是4KB.在x86平臺下,默認(rèn)的線程棧大小是12KB.
ThreadName,可以通過創(chuàng)建Thread實(shí)例后調(diào)用,setName方法設(shè)置。默認(rèn)線程名:Thread-N,N是線程建立的順序,是一個(gè)不重復(fù)的正整數(shù),它的來源基于Thread.nextThreadNum()
/* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
stackSize是一種具有平臺依賴性的參數(shù),它能指定堆棧的大小。 在某些平臺上,指定一個(gè)較高的 stackSize 參數(shù)值可能使線程在拋出 StackOverflowError 之前達(dá)到較大的遞歸深度。stackSize 參數(shù)的值與最大遞歸深度和并發(fā)程度之間的關(guān)系細(xì)節(jié)與平臺有關(guān)。在某些平臺上,stackSize 參數(shù)的值無論如何不會起任何作用。作為建議,可以讓虛擬機(jī)自由處理 stackSize 參數(shù)。
ThreadGroup group = new ThreadGroup("battcn-group"); Thread t1 = new Thread(() -> System.out.println("hello my name"s" + Thread.currentThread().getName() + " group name"s" + Thread.currentThread().getThreadGroup().getName())); Thread t2 = new Thread(() -> System.out.println("hello my name"s" + Thread.currentThread().getName() + " group name"s" + Thread.currentThread().getThreadGroup().getName()), "thread-battcn2"); Thread t3 = new Thread(() -> System.out.println("hello my name"s" + Thread.currentThread().getName() + " group name"s" + Thread.currentThread().getThreadGroup().getName()), "thread-battcn3"); t1.start(); t2.start(); t3.start(); group.enumerate(new Thread[]{t2, t3});
以下是上部代碼片段的日志輸出,可以看到指定ThreadName與ThreadGroup以及不指定的的區(qū)別
hello my name"s Thread-0 group name"smain hello my name"s thread-battcn2 group name"smain hello my name"s thread-battcn3 group name"smain
警告 如果對stackSize 有興趣的可以試試下面代碼,不過慎重,有可能吧你電腦內(nèi)存跑完....
for (int i = 0; i < Integer.MAX_VALUE; i++) { new Thread(group, new Runnable() { @Override public void run() { try { add(1); } catch (Error error) { System.out.println(count); error.printStackTrace(); } } private void add(int i) { count++; add(i + 1); } }, "thread-battcn-4",1 << 24).start(); }- 說點(diǎn)什么
微信公眾號:battcn(歡迎調(diào)戲)
全文代碼:https://git.oschina.net/battcn/battcn-concurent
個(gè)人QQ:1837307557
battcn開源群(適合新手):3916196
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70510.html
摘要:文本將介紹兩種可以優(yōu)雅的終止線程的方式第一種在多線程模式中有一種叫兩步終止的模式可以優(yōu)雅的終止線程,這種模式采用了兩個(gè)步驟來終止線程,所以叫兩步終止模式。 Java中原來在Thread中提供了stop()方法來終止線程,但這個(gè)方法是不安全的,所以一般不建議使用。文本將介紹兩種可以優(yōu)雅的終止線程的方式... 第一種 在JAVA《Java多線程模式》中有一種叫Two-Phase Term...
摘要:現(xiàn)在已經(jīng)沒有強(qiáng)制線程終止的方法了由于方法可以讓一個(gè)線程終止掉另一個(gè)線程被終止的線程會立即釋放鎖,這可能會讓對象處于不一致的狀態(tài)。 前言 昨天已經(jīng)寫了: 多線程三分鐘就可以入個(gè)門了! 如果沒看的同學(xué)建議先去閱讀一遍哦~ 在寫文章之前通讀了一遍《Java 核心技術(shù) 卷一》的并發(fā)章節(jié)和《Java并發(fā)編程實(shí)戰(zhàn)》前面的部分,回顧了一下以前寫過的筆記。從今天開始進(jìn)入多線程的知識點(diǎn)咯~ 我其實(shí)也是相...
摘要:當(dāng)時(shí),會進(jìn)入循環(huán),系統(tǒng)會判斷主線程是否處于活躍狀態(tài),如果處于活躍狀態(tài),主線程就會不停的等待。 由于前段時(shí)間比較忙,線程這快學(xué)習(xí)停滯了,只能利用周日的時(shí)間來寫寫博客了,多線程Join方法的作用就是把指定的線程加入到當(dāng)前線程,讓主線程等待子線程結(jié)束之后才能繼續(xù)運(yùn)行,從而完成同步操作 介紹 join() 的作用:讓主線程等待子線程結(jié)束之后才能繼續(xù)運(yùn)行,首先先來看下以采集為案例的代碼,統(tǒng)計(jì)采...
摘要:在之前,不能為線程單獨(dú)設(shè)置或指定一個(gè)默認(rèn)的,為了設(shè)置,需要繼承并覆寫方法。幸運(yùn)的是后線程提供了一個(gè)方法,用來捕獲并處理因線程中拋出的未知異常,以避免程序終止。 在單線程的開發(fā)過程中,通常采用try-catch的方式進(jìn)行異常捕獲,但是這種方式在多線程環(huán)境中會顯得無能為力,而且還有可能導(dǎo)致一些問題的出現(xiàn),比如發(fā)生異常的時(shí)候不能及時(shí)回收系統(tǒng)資源,或者無法及時(shí)關(guān)閉當(dāng)前的連接... 概述 Ja...
摘要:一般差異簡單來說,是一個(gè)用于線程同步的實(shí)例方法。暫停當(dāng)前線程,不釋放任何鎖。用來線程間通信,使擁有該對象鎖的線程等待直到指定時(shí)間或。執(zhí)行對該對象加的同步代碼塊。 在JAVA的學(xué)習(xí)中,不少人會把sleep和wait搞混,認(rèn)為都是做線程的等待,下面主要介紹下這倆者是什么,及了解它們之間的差異和相似之處。 一般差異 簡單來說,wait()是一個(gè)用于線程同步的實(shí)例方法。因?yàn)槎x在java.l...
閱讀 2259·2023-04-26 01:50
閱讀 714·2021-09-22 15:20
閱讀 2595·2019-08-30 15:53
閱讀 1596·2019-08-30 12:49
閱讀 1714·2019-08-26 14:05
閱讀 2714·2019-08-26 11:42
閱讀 2309·2019-08-26 10:40
閱讀 2602·2019-08-26 10:38