前言
在運行一個Java線程之前需要構(gòu)造一個線程對象,在構(gòu)造線程對象的時候需要提供線程需要的屬性庄吼,比如線程組缎除、優(yōu)先級、是否是Daemon線程等信息总寻。調(diào)用start()
去啟動線程器罐,是當前線程(parent線程)告知Java虛擬機,只要線程規(guī)劃器空閑渐行,就啟動調(diào)用了start()
方法的線程轰坊。
接下來對以下7點做個概括:
- 線程狀態(tài)
- Daemon線程
- 線程中斷
- 過期的suspend()铸董、resume()、stop()
- 安全地終止線程
- 線程間通信(volatile肴沫、synchronized粟害、等待/通知、管道颤芬、join())
- ThreadLocal
線程狀態(tài)
Java線程有6種不同的狀態(tài):
狀態(tài)名稱 | 說明 |
---|---|
NEW | 初始狀態(tài)悲幅,線程被創(chuàng)建,但是還沒有調(diào)用start()方法 |
RUNNABLE | 運行狀態(tài)站蝠,Java線程將操作系統(tǒng)中的就緒和運行統(tǒng)稱為“運行中” |
BLOCKED | 阻塞狀態(tài)汰具,表示線程阻塞于鎖 |
WAITING | 等待狀態(tài),表示線程進入等待狀態(tài)菱魔,需要其他線程進行一些特定操作(通知或中斷)才能返回 |
TIME_WAITING | 超時等待狀態(tài)留荔,與WAITING不同,超時后會自動返回 |
TERMINATED | 終止狀態(tài)澜倦,表示當前線程已經(jīng)執(zhí)行完畢 |
狀態(tài)之間的變遷如下:
- 線程調(diào)用
start()
方法之后開始運行聚蝶; - 當調(diào)用線程的
wait()
方法之后進入等待狀態(tài),等待狀態(tài)下的線程需要其他線程調(diào)用notify()/notifyAll()
方法之后才能返回運行狀態(tài)肥隆。而超時等待狀態(tài)加了超時限制既荚,達到超時時間之后會返回運行狀態(tài)稚失; - 當線程調(diào)用同步方法時栋艳,沒有獲得鎖的情況下,會進入阻塞狀態(tài)句各,獲取到鎖之后返回運行狀態(tài)吸占;
- 線程執(zhí)行完Runable的
run()
方法之后進入終止狀態(tài); - 阻塞狀態(tài)是線程進入
synchronized
關(guān)鍵字修飾的方法或者代碼塊時的狀態(tài)凿宾;阻塞在java.concurrent
包下的Lock
接口則是等待狀態(tài)矾屯。
Daemon線程
Daemon線程是支持性線程,在程序后臺進行調(diào)度以及支持性工作初厚。這意味著件蚕,當虛擬機不存在非Daemon線程,那么Java虛擬機將會退出产禾。Daemon線程中finally
塊的內(nèi)容將不會執(zhí)行排作,因此不能在Daemon線程的finally
塊中做資源釋放工作。
通過Thread.setDaemon(true)
來設(shè)置Daemon線程亚情,需要在start()
方法調(diào)用之前設(shè)置妄痪。
線程中斷
中斷其實是線程中的一個標志位,其他線程通過調(diào)用該線程對象的interrupt()
方法對其進行中斷操作楞件。線程對象通過檢查自身是否被中斷來進行響應(yīng)衫生,線程對象通過方法isInterrupted()
來判斷是否被中斷裳瘪,也可以通過Thread.interrupted()
來復位中斷標記位。如果線程處于終結(jié)狀態(tài)罪针,即使被調(diào)用了interrupt()
方法彭羹,該線程對象的isInterrupted()
返回的依然是false。還有許多聲明拋出InterruptedException
的方法(例如sleep(long millis)
方法)在拋出異常之前會清空中斷標識位(設(shè)置為false)站故。
過期的suspend()皆怕、resume()、stop()
調(diào)用suspend()
之后西篓,線程不會釋放已經(jīng)占有的資源(比如鎖)愈腾,而是占有著資源進入睡眠狀態(tài),這樣容易引發(fā)死鎖問題岂津。調(diào)用stop()
方法在終結(jié)一個線程時不會保證線程的資源正常釋放虱黄,通常是沒有給予線程完成資源釋放工作的機會,因此會導致程序可能工作在不確定狀態(tài)下吮成。
安全地終止線程
線程的掛起suspend()
方法以及停止stop()
方法都不能安全地終止線程橱乱,那么只通過中斷interrupt()
方法來實現(xiàn)安全終止。
class SaveShutdownRunnable implements Runnable {
private volatile boolean mShutDown;
private long i;
@Override
public void run() {
while (!mShutDown && !Thread.currentThread().isInterrupted()) {
i++;
}
}
public void cancel(){
mShutDown = true;
}
}
線程間通信(volatile粱甫、synchronized泳叠、等待/通知、管道茶宵、join())
任意一個對象都擁有自己的監(jiān)視器危纫,當這個對象由同步塊或者這個對象的同步方法調(diào)用時,執(zhí)行方法的線程必須先獲取該對象的監(jiān)視器才能進入同步塊或者同步方法乌庶,而沒有獲取到監(jiān)視器(執(zhí)行該方法)的線程將會被阻塞在同步塊和同步方法的入口處种蝶,進入BLOCKED狀態(tài)瞒大。
等待/通知機制,是指一個線程A調(diào)用了對象O的wait()
方法進入等待狀態(tài)透敌,而另一個線程B調(diào)用了對象O的notify()
或者notifyAll()
方法酗电,線程A接收到通知之后從對象O的wait()
方法返回,繼續(xù)執(zhí)行后續(xù)的操作泼疑。
等待/通知機制依托于同步機制,其目的就是確保等待線程從wait()
方法返回時能夠感知到通知線程對變量做出的修改移稳』嵊停可以得出如下細節(jié):
- 使用
wait()
、notify()
和notifyAll()
時需要先對調(diào)用對象加鎖都许; - 調(diào)用
wait()
方法后嫂冻,線程狀態(tài)由RUNNING變成WAITING,并將當期線程放置到對象的等待隊列; -
notify()
或notifyAll()
方法調(diào)用后睛低,等待線程依舊不會從wait()
返回服傍,需要調(diào)用notify()
或notifyAll()
的線程釋放鎖之后,等待線程才有機會從wait()
返回吹零; -
notify()
方法將等待隊列中的一個等待線程從等待隊列中移到同步隊列中灿椅,而notifyAll()
方法是將等待隊列中所有的線程全部移到同步隊列,被移動的線程狀態(tài)由WAITING變?yōu)锽LOCKED泣懊; - 從
wait()
方法返回的前提是獲得調(diào)用對象的鎖麻惶。
等待/通知機制.png
等待/通知的經(jīng)典范式 - 等待方偽代碼:
// 獲得對象的鎖
synchronized(對象){
// 條件不滿足則調(diào)用對象的wait()方法
while(條件不滿足){
對象.wait();
}
對應(yīng)的處理邏輯
}
- 通知方偽代碼
// 獲得對象的鎖
synchronized(對象){
改變條件
對象.notifyAll();
}