只有runnable到running時纔會佔用cpu時間片,其餘都會出讓cpu時間片。
線程的資源有很多,但應該包含CPU資源和鎖資源這兩類。
sleep(long mills):讓出CPU資源,可是不會釋放鎖資源。
wait():讓出CPU資源和鎖資源。html
1. Thread.sleep(long) 和Thread.yield()都是Thread類的靜態方法,在調用的時候都是Thread.sleep(long)/ Thread.yield()的方式進行調用。java
而join()是由線程對象來調用。面試
2.wait()和notify()、notifyAll() 這三個方法都是java.lang.Object的方法! 多線程
Object 是java.lang.Object,由於每天說Java是面向對象的,因此Object是全部Java對象的超類,都實現Object的方法:ide
Object參考:Java超類-java.lang.objectpost
它們都是用於協調多個線程對共享數據的存取,因此必須在Synchronized語句塊內使用這三個方法。前面說過Synchronized這個關鍵字用於保護共享數據,阻止其餘線程對共享數據的存取。可是這樣程序的流程就很不靈活了,如何才能在當前線程還沒退出Synchronized數據塊時讓其餘線程也有機會訪問共享數據呢?此時就用這三個方法來靈活控制。 url
(1) wait()方法使當前線程暫停執行並釋放對象鎖標誌,讓其餘線程能夠進入Synchronized數據塊,當前線程被放入對象等待池中。spa
(2) 當調用 notify()方法後,將從對象的等待池中移走一個任意的線程並放到鎖標誌等待池中,只有鎖標誌等待池中的線程可以獲取鎖標誌;若是鎖標誌等待池中沒有線程,則notify()不起做用。
(3) notifyAll()則從對象等待池中移走全部等待那個對象的線程並放到鎖標誌等待池中。 .net
sleep與Wait的區別:sleep是線程方法,wait是object方法;看區別,主要是看CPU的運行機制:線程
它們的區別主要考慮兩點:1.cpu是否繼續執行、2.鎖是否釋放掉。
對於這兩點,首先解釋下cpu是否繼續執行的含義:cpu爲每一個線程劃分時間片去執行,每一個時間片時間都很短,cpu不停地切換不一樣的線程,以看似他們好像同時執行的效果。
其次解釋下鎖是否釋放的含義:鎖若是被佔用,那麼這個執行代碼片斷是同步執行的,若是鎖釋放掉,就容許其它的線程繼續執行此代碼塊了。
明白了以上兩點的含義,開始分析sleep和wait:
sleep ,釋放cpu資源,不釋放鎖資源,若是線程進入sleep的話,釋放cpu資源,若是外層包有Synchronize,那麼此鎖並無釋放掉。
wait,釋放cpu資源,也釋放鎖資源,通常用於鎖機制中 確定是要釋放掉鎖的,由於notify並不會當即調起此線程,所以cpu是不會爲其分配時間片的,也就是說wait 線程進入等待池,cpu不分時間片給它,鎖釋放掉。
(wait用於鎖機制,sleep不是,這就是爲啥sleep不釋放鎖,wait釋放鎖的緣由,sleep是線程的方法,跟鎖沒半毛錢關係,wait,notify,notifyall 都是Object對象的方法,是一塊兒使用的,用於鎖機制)
最後:
1.sleep:Thread類的方法,必須帶一個時間參數。會讓當前線程休眠進入阻塞狀態並釋放CPU(阿里面試題 Sleep釋放CPU,wait 也會釋放cpu,由於cpu資源太寶貴了,只有在線程running的時候,纔會獲取cpu片斷),提供其餘線程運行的機會且不考慮優先級,但若是有同步鎖則sleep不會釋放鎖即其餘線程沒法得到同步鎖 可經過調用interrupt()方法來喚醒休眠線程。
2.yield:讓出CPU調度,Thread類的方法,相似sleep只是不能由用戶指定暫停多長時間 ,而且yield()方法只能讓同優先級的線程有執行的機會。 yield()只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行。調用yield方法只是一個建議,告訴線程調度器個人工做已經作的差很少了,可讓別的相同優先級的線程使用CPU了,沒有任何機制保證採納。
3.wait:Object類的方法(notify()、notifyAll() 也是Object對象),必須放在循環體和同步代碼塊中,執行該方法的線程會釋放鎖,進入線程等待池中等待被再次喚醒(notify隨機喚醒,notifyAll所有喚醒,線程結束自動喚醒)即放入鎖池中競爭同步鎖
4.join:一種特殊的wait,當前運行線程調用另外一個線程的join方法,當前線程進入阻塞狀態直到另外一個線程運行結束等待該線程終止。 注意該方法也須要捕捉異常。
等待調用join方法的線程結束,再繼續執行。如:t.join();//主要用於等待t線程運行結束,若無此句,main則會執行完畢,致使結果不可預測。
關於Java中線程的生命週期,首先看一下下面這張較爲經典的圖:
上圖中基本上囊括了Java中多線程各重要知識點。掌握了上圖中的各知識點,Java中的多線程也就基本上掌握了。主要包括:
線程的5個狀態
一、新建狀態(New):當線程對象對建立後,即進入了新建狀態,如:Thread t = new MyThread();
二、就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經作好了準備,隨時等待CPU調度執行,獲取cpu 的使用權,並非說執行了t.start()此線程當即就會執行
三、運行狀態(Running):可運行狀態(runnable)的線程得到了cpu 時間片(timeslice) ,執行程序代碼。 當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就 緒狀態是進入到運行狀態的惟一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;
四、阻塞狀態(Blocked):處於運行狀態中的線程因爲某種緣由,暫時放棄對CPU的使用權,,也即讓出了cpu timeslice, 中止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。纔有機會再次得到cpu timeslice 轉到運行(running)狀態 根據阻塞產生的緣由不一樣,阻塞狀態又能夠分爲三種:
1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;,JVM會把該線程放入等待隊列(waitting queue)中。
2.同步阻塞 –,運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,獲取synchronized同步鎖失敗 , 它會進入同步阻塞狀態 ,則JVM會把該線程放入鎖池(lock pool)中。
3.其餘阻塞 – 經過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入可運行(runnable)狀態。
五、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
從圖中能夠看出,只有runnable到running時纔會佔用cpu時間片,其餘都會出讓cpu時間片。
線程的資源有很多,但應該包含CPU資源和鎖資源這兩類。
sleep(long mills):讓出CPU資源,可是不會釋放鎖資源。
wait():讓出CPU資源和鎖資源。
鎖是用來線程同步的,sleep(long mills)雖然讓出了CPU,可是不會讓出鎖,其餘線程能夠利用CPU時間片了,但若是其餘線程要獲取sleep(long mills)擁有的鎖才能執行,則會由於沒法獲取鎖而不能執行,繼續等待。
可是那些沒有和sleep(long mills)競爭鎖的線程,一旦獲得CPU時間片便可運行了。
5. 死亡(DEAD):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。
我寫了個例子:
public class abc_test { public static void main(String[] args) { // TODO Auto-generated method stub Thread thread =new Thread(new joinDemo()); thread.start(); for(int i=0;i<20;i++){ System.out.println("主線程第"+i+"此執行!"); if(i>=2){ try{ //t1線程合併到主線程中,主線程中止執行過程,轉而執行t1線程,直到t1執行完畢後繼續; thread.join(); }catch(InterruptedException e){ e.printStackTrace(); } } } } } class joinDemo implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++){ System.out.println("線程1第"+i+"次執行"); } } }
結果爲:
主線程第0此執行!
主線程第1此執行!
主線程第2此執行!
線程1第0次執行
線程1第1次執行
線程1第2次執行
線程1第3次執行
線程1第4次執行
線程1第5次執行
線程1第6次執行
線程1第7次執行
線程1第8次執行
線程1第9次執行
主線程第3此執行!
主線程第4此執行!
主線程第5此執行!
主線程第6此執行!
.....
主線程第19此執行!
也可參考我專門寫的關於線程狀態的文章:Java線程的5種狀態及切換(透徹講解)-京東面試
參考:主題:sleep,wait,join,yield有何差異?
參考:java之yield(),sleep(),wait()區別詳解-備忘筆記
參考:sleep,yield,join,notify,wait,notifyAll區別
參考:Java線程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和區別