Java Thread 多執行緒 操作執行緒
阿新 • • 發佈:2018-12-30
建立、啟動執行緒
執行緒的實現方式
執行緒的生命週期
執行緒的狀態
控制執行緒
5、執行緒的建立和啟動
A、繼承Thread類或實現Runnable介面,重寫或實現run方法,run方法代表執行緒要完成的任務
B、建立Thread子類或是Runnable的實現類,即建立的執行緒物件;不同的是介面實現執行緒,
需要將介面的實現類作為引數傳遞給Thread類的構造引數
C、用執行緒物件的start方法啟動執行緒
6、繼承Thread和實現Runnable介面建立執行緒的區別
採用Runnable介面實現執行緒:
優勢:
A、執行緒類只是實現了Runnable介面,還可以繼承其他的類
B、在這種方式下,可以多個執行緒共享同一個目標物件,所以很合適多個執行緒來處理同一份資源的情況,
從而可以將CPU、程式碼和資料分開,形成清晰的模型,較好的面相物件思想。
劣勢:程式設計稍微複雜,如果需要訪問當前執行緒需要用Thread.currentThread方法來獲取
採用繼承Thread類的方式實現執行緒:
優勢:編寫簡單,如果要獲得當前執行緒直接this即可
劣勢:執行緒類繼承了Thread,不能在繼承其他類
相對而言,用Runnable的方式更好,具體可以根據當前需要而定;
7、執行緒生命週期
執行緒被建立啟動後,不併不是啟動後就進入了執行狀態,也不是一直處於的執行狀態。
執行緒的生命週期分為建立(new)、就緒(Runnable)、執行(running)、阻塞(Blocked)、死亡(Dead)五種狀態。
執行緒啟動後不會一直霸佔CPU資源,所以CPU需要在多條執行緒中切換執行,執行緒就會在多次的執行和阻塞中切換。
8、新建(new)和就緒(Runnable)狀態
當new一個執行緒後,該執行緒處於新建狀態,此時它和Java物件一樣,僅僅由Java虛擬機器為其分配記憶體空間,並初始化成員變數。
此時執行緒物件沒有表現出任何的動態特徵,程式也不會執行執行緒的執行體。
注意:run方法是執行緒的執行體,不能由我們手動呼叫。我們可以用start方法啟動執行緒,系統會把run方法當成執行緒的執行體來執行,
如果直接呼叫執行緒物件run方法,則run方法立即會被執行。而且在run方法返回之前其他執行緒無法並行執行,
也就是說系統會把當前執行緒類當成一個普通的Java物件,而run方法也是一個普通的方法,而不是執行緒的執行體。
9、執行(running)和阻塞(Blocked)狀態
如果處於就緒狀態的執行緒就獲得了CPU,開始執行run方法的執行緒執行體,則該執行緒處於執行狀態。
單CPU的機器,任何時刻只有一條執行緒處於執行狀態。當然,在多CPU機器上將會有多執行緒並行(parallel)執行,
當執行緒大於CPU數量時,依然會在同一個CPU上切換執行。
執行緒執行機制:一個執行緒執行後,它不可能一直處於執行狀態(除非它執行的時間很短,瞬間執行完成),執行緒在執行過程中需要中斷,
目的是讓其他的執行緒有執行機會,執行緒的排程取決於底層的策略。對應搶佔式的系統而言,系統會給每個可執行的執行緒一個小時間段來處理任務,
當時間段到達系統就會剝奪該執行緒的資源,讓其他的執行緒有執行的機會。在選擇下一個執行緒時,系統會考慮執行緒優先順序。
以下情況會出現執行緒阻塞狀態:
A、執行緒呼叫sleep方法,主動放棄佔用的處理器資源
B、執行緒呼叫了阻塞式IO方法,在該方法返回前,該執行緒被阻塞
C、執行緒試圖獲得一個同步監視器,但該同步監視器正被其他執行緒所持有。
D、執行緒等待某個通知(notify)
E、程式呼叫了suspend方法將該執行緒掛起。不過這個方法容易導致死鎖,儘量不免使用該方法
當執行緒被阻塞後,其他執行緒將有機會執行。被阻塞的執行緒會在合適的時候重新進入就緒狀態,注意是就緒狀態不是執行狀態。
也就是被阻塞執行緒在阻塞解除後,必須重新等待執行緒排程器再次呼叫它。
針對上面執行緒阻塞的情況,發生以下特定的情況可以解除阻塞,讓程序進入就緒狀態:
A、呼叫sleep方法的經過了指定的休眠時間
B、執行緒呼叫的阻塞IO已經返回,阻塞方法執行完畢
C、執行緒成功獲得了試圖同步的監視器
D、執行緒正在等待某個通知,其他執行緒發出了通知
E、處於掛起狀態的執行緒呼叫了resume恢復方法
執行緒從阻塞狀態只能進入就緒狀態,無法進入執行狀態。而就緒和執行狀態之間的轉換通常不受程式控制,而是由系統排程所致的。
當就緒狀態的執行緒獲得資源時,該執行緒進入執行狀態;當執行狀態的執行緒事情處理器資源時就進入了就緒狀態。
但對呼叫了yield的方法就例外,此方法可以讓執行狀態轉入就緒狀態。
10、執行緒死亡(Dead)狀態
執行緒會在以下方式進入死亡狀態:
A、run方法執行完成,執行緒正常結束
B、執行緒丟擲未捕獲的異常或Error
C、直接呼叫該執行緒的stop方法來結束執行緒—該方法易導致死鎖,注意使用
注意:當主執行緒結束的時候,其他執行緒不受任何影響。一旦子執行緒啟動後,會擁有和主執行緒相同的地位,不受主執行緒影響。
isAlive方法可以測試當前執行緒是否死亡,當執行緒處於就緒、執行、阻塞狀態,該方法返回true,如果執行緒處於新建或死亡狀態就會返回false。
不要試圖對死亡的執行緒呼叫start方法,來啟動它。死亡執行緒不可能再次執行。
11、控制執行緒
Java執行緒提供了很多工具方法,這些方法都很好的控制執行緒
A、join執行緒
讓一個執行緒等待另一個執行緒完成的方法。當某個程式執行流中呼叫其他執行緒的join方法時,呼叫執行緒將會被阻塞,直到被join方法的join執行緒執行完成為止。
join方法通常有使用執行緒的程式呼叫,將大問題劃分成許多小問題。每個小問題分配一個執行緒。當所有的小問題得到處理後,再呼叫主執行緒進一步操作。
join有三種過載模式:
一、join等待被join的執行緒執行完成
二、join(long millis)等待被join的執行緒時間最長為millis毫秒,如果在millis毫秒外,被join的執行緒還沒有執行完則不再等待
三、join(long millis, int nanos)被join的執行緒等待時間長為millis毫秒加上nanos微秒
通常我們很少用第三種join,原因有二:程式對時間的精度無需精確到千分之一毫秒
計算機硬體、作業系統也無法做到精確到千分之一毫秒
B、後臺執行緒
有一種執行緒,在後臺執行,它的任務是為其他執行緒提供服務,這種執行緒被稱為“後臺執行緒(Daemon Thread)”,有被稱為“守護執行緒”或“精靈執行緒”。
JVM的垃圾回收器執行緒就是後臺程序。
後臺程序有個特徵是:如果前臺的程序都死亡,那麼後臺程序也死亡。(它為前臺程序服務)
用Thread的setDaemon (true)方法可以指定當前執行緒為後臺執行緒。
注意:前臺執行緒執行完成死亡後,JVM會通知後臺執行緒,後臺執行緒就會死亡。但它得到通知到後臺執行緒作成響應,需要一段時間,
而且要將某個執行緒設定為後臺執行緒,必需要在該執行緒啟動前設定,也就是說設定setDaemon必需在start方法前面呼叫。
否則會出現java.lang.IllegalThreadStateException異常
C、執行緒休眠sleep
如果需要當前執行緒暫停一段時間,並進入阻塞狀態就需要用sleep,sleep有2中過載方式:
sleep(long millis)讓當前執行緒暫停millis毫秒後,並進入阻塞狀態,該方法受系統計時器和執行緒排程器的影響
sleep(long millis, int nanos)讓當前正在執行的執行緒暫停millis毫秒+nanos微秒,並進入阻塞
當呼叫sleep方法進入阻塞狀態後,在sleep時間段內,該執行緒不會獲得執行機會,即使沒有其他可執行的執行緒,處於sleep的執行緒不會執行。
D、執行緒讓步yield
yield和sleep有點類似,它也可以讓當前執行的執行緒暫停,但它不會阻塞執行緒,只是將該執行緒轉入到就緒狀態。
yield只是讓當前執行緒暫停下,讓系統執行緒排程器重新排程下。
當yield的執行緒後,當前執行緒暫停。系統執行緒排程器會讓優先順序相同或是更高的執行緒執行。
sleep和yield的區別
(1)、sleep方法暫停當前執行緒後,會給其他執行緒執行集合,不會理會執行緒的優先順序。但yield則會給優先順序相同或高優先順序的執行緒執行機會
(2)、sleep方法會將執行緒轉入阻塞狀態,直到經過阻塞時間才會轉入到就緒狀態;而yield則不會將執行緒轉入到阻塞狀態,它只是強制當前執行緒進入就緒狀態。
因此完全有可能呼叫yield方法暫停之後,立即再次獲得處理器資源繼續執行。
(3)、sleep宣告丟擲了InterruptedException異常,所以呼叫sleep方法時,要麼捕獲異常,要麼丟擲異常。而yield沒有申明丟擲任何異常
E、改變執行緒優先順序
每個執行緒都有優先順序,優先順序決定執行緒的執行機會的多少。
每個執行緒預設和它建立的父類的優先順序相同,main方法的優先順序是普通優先順序,那在main方法中建立的子執行緒都是普通優先順序。
getPriority(int newPriority)/setPriority(int)
設定優先順序有以下級別:
MAX_PRIORITY 值是10
MIN_PRIORITY 值是1
NORM_PRIORITY 值是5
範圍是1-10;