線程狀态轉化圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLygzMygjM1AzMx0SO0UDO0EDMyIDNxYDM5EDMy0iN5UzN2MTMvwlNwkTMwIzLcZTO1cjNzEzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
說明:
線程總共包括以下5種狀态。
1、新狀态New:該狀态也叫建立狀态,當線程對象被建立後,線程就進入了建立狀态。例如:Thread thread = new Thread();。
2、就緒狀态Runnable:該狀态也被稱為可執行狀态。當線程對象被建立以後,其它線程調用了該對象的start()方法,進而來啟用該線程。例如:thread.start()。處于就緒狀态的線程,随時可能被CPU排程執行。
3、運作狀态(Running):線程擷取CPU權限進行執行。但注意的是線程隻能從就緒狀态進入到運作狀态。
4、阻塞狀态(Blocked):阻塞狀态時線程因為某種原因放棄了CPU的使用權,暫時停止運作。直到線程進入到就緒狀态才有機會轉到運作狀态。
阻塞的情況有三種:
(1) 等待阻塞--同過調用線程的wait()方法,讓線程等待某工作的完成。
(2) 同步阻塞 -- 線程在擷取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀态。
(3) 其他阻塞 -- 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀态。當sleep()狀态逾時、join()等待線程終止或者逾時、或者I/O處理完畢時,線程重新轉入就緒狀态。
5、死亡狀态(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
一、wait(),notify(),notify(),和notifyAll()等方法介紹。
在Object.java中,定義了wait(), notify()和notifyAll()等接口。
wait()的作用是讓目前線程進入等待狀态,同時,wait()也會讓目前線程釋放它所持有的鎖。
notify()和notifyAll()的作用,則是喚醒目前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程。
Object類中關于等待/喚醒的API詳細資訊如下:
notify() -- 喚醒在此對象螢幕上等待的單個線程。
notifyAll() -- 喚醒在此對象螢幕上等待的所有線程。
wait() -- 讓目前線程處于“等待(阻塞)狀态”,“直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法”,目前線程被喚醒(進入“就緒狀态”)。
wait(long timeout) -- 讓目前線程處于“等待(阻塞)狀态”,“直到其他線程調用此對象的 notify() 方法 或 notifyAll() 方法,或者超過指定的時間量”,目前線程被喚醒(進入“就緒狀 态”)。
wait(long timeout, int nanos) -- 讓目前線程處于“等待(阻塞)狀态”,“直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷目前線程,或者已超過某個實際時間量”,目前線程被喚醒(進入“就緒狀态”)。
二、為什麼notify(), wait()等函數定義在Object中,而不是Thread中。
Object中的wait(), notify()等函數,和synchronized一樣,會對“對象的同步鎖”進行操作。
wait()會使“目前線程”等待,因為線程進入等待狀态,是以線程應該釋放它鎖持有的“同步鎖”,否則其它線程擷取不到該“同步鎖”而無法運作!
OK,線程調用wait()之後,會釋放它鎖持有的“同步鎖”;而且,根據前面的介紹,我們知道:等待線程可以被notify()或notifyAll()喚醒。現在,請思考一個問題:notify()是依據什麼喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什麼關聯起來的?答案是:依據“對象的同步鎖”。
負責喚醒等待線程的那個線程(我們稱為“喚醒線程”),它隻有在擷取“該對象的同步鎖”(這裡的同步鎖必須和等待線程的同步鎖是同一個),并且調用notify()或notifyAll()方法之後,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執行,因為喚醒線程還持有“該對象的同步鎖”。必須等到喚醒線程釋放了“對象的同步鎖”之後,等待線程才能擷取到“對象的同步鎖”進而繼續運作。
總之,notify(), wait()依賴于“同步鎖”,而“同步鎖”是對象鎖持有,并且每個對象有且僅有一個!這就是為什麼notify(), wait()等函數定義在Object類,而不是Thread類中的原因。
三、 yield()介紹
yield()的作用是讓步。它能讓目前線程由“運作狀态”進入到“就緒狀态”,進而讓其它具有相同優先級的等待線程擷取執行權;但是,并不能保證在目前線程調用yield()之後,其它具有相同優先級的線程就一定能獲得執行權;也有可能是目前線程又進入到“運作狀态”繼續運作!
四、 yield() 與 wait()的比較
我們知道,wait()的作用是讓目前線程由“運作狀态”進入“等待(阻塞)狀态”的同時,也會釋放同步鎖。而yield()的作用是讓步,它也會讓目前線程離開“運作狀态”。它們的差別是:
(01) wait()是讓線程由“運作狀态”進入到“等待(阻塞)狀态”,而不yield()是讓線程由“運作狀态”進入到“就緒狀态”。
(02) wait()是會線程釋放它所持有對象的同步鎖,而yield()方法不會釋放鎖。
1 package com.zjk.thread;
2
3 public class YieldLockTest {
4 private static Object obj = new Object();
5
6 public static void main(String[] args) {
7 ThreadA t1 = new ThreadA("t1");
8 ThreadA t2 = new ThreadA("t2");
9 t1.start();
10 t2.start();
11 }
12
13 static class ThreadA extends Thread{
14 public ThreadA (String name) {
15 super(name);
16 }
17 public void run () {
18 //擷取obj對象的同步
19 synchronized (obj) {
20 for(int i=0; i <10; i++){
21 System.out.printf("%s [%d]:%d\n", this.getName(), this.getPriority(), i);
22 // i整除4時,調用yield
23 if (i%4 == 0)
24 Thread.yield();
25 }
26 }
27 }
28 }
29
30 }
輸出結果:
1 t1 [5]:0
2 t1 [5]:1
3 t1 [5]:2
4 t1 [5]:3
5 t1 [5]:4
6 t1 [5]:5
7 t1 [5]:6
8 t1 [5]:7
9 t1 [5]:8
10 t1 [5]:9
11 t2 [5]:0
12 t2 [5]:1
13 t2 [5]:2
14 t2 [5]:3
15 t2 [5]:4
16 t2 [5]:5
17 t2 [5]:6
18 t2 [5]:7
19 t2 [5]:8
20 t2 [5]:9
說明:
主線程main中啟動了兩個線程t1和t2。t1和t2在run()會引用同一個對象的同步鎖,即synchronized(obj)。在t1運作過程中,雖然它會調用Thread.yield();但是,t2是不會擷取cpu執行權的。因為,t1并沒有釋放“obj所持有的同步鎖”!
五、sleep()介紹
sleep()定義在Thread.java中。
sleep() 的作用是讓目前線程休眠,即目前線程會從“運作狀态”進入到“休眠(阻塞)狀态”。sleep()會指定休眠時間,線程休眠的時間會大于/等于該休眠時間;線上程重新被喚醒時,它會由“阻塞狀态”變成“就緒狀态”,進而等待cpu的排程執行。
六、sleep()與wait()的比較
我們知道,wait()的作用是讓目前線程由“運作狀态”進入“等待(阻塞)狀态”的同時,也會釋放同步鎖。而sleep()的作用是也是讓目前線程由“運作狀态”進入到“休眠(阻塞)狀态”。
但是,wait()會釋放對象的同步鎖,而sleep()則不會釋放鎖。
轉載于:https://www.cnblogs.com/liuhuijie/p/11025728.html