JAVA 執行緒狀態及轉化(轉)

jackWang發表於2019-03-31

原文連線:www.cnblogs.com/happy-coder…

執行緒狀態圖

JAVA 執行緒狀態及轉化(轉)

說明: 執行緒共包括以下5種狀態。

  1. 新建狀態(New) : 執行緒物件被建立後,就進入了新建狀態。例如,Thread thread = new Thread()。

  2. 就緒狀態(Runnable): 也被稱為“可執行狀態”。執行緒物件被建立後,其它執行緒呼叫了該物件的start()方法,從而來啟動該執行緒。例如,thread.start()。處於就緒狀態的執行緒,隨時可能被CPU排程執行。

  3. 執行狀態(Running) : 執行緒獲取CPU許可權進行執行。需要注意的是,執行緒只能從就緒狀態進入到執行狀態。

  4. 阻塞狀態(Blocked) : 阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種: (01) 等待阻塞 -- 通過呼叫執行緒的wait()方法,讓執行緒等待某工作的完成。 (02) 同步阻塞 -- 執行緒在獲取synchronized同步鎖失敗(因為鎖被其它執行緒所佔用),它會進入同步阻塞狀態。 (03) 其他阻塞 -- 通過呼叫執行緒的sleep()或join()或發出了I/O請求時,執行緒會進入到阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。

  5. 死亡狀態(Dead) : 執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。

  6. wait(), 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() 方法,或者其他某個執行緒中斷當前執行緒,或者已超過某個實際時間量”,當前執行緒被喚醒(進入“就緒狀態”)。

  1. 為什麼notify(), wait()等函式定義在Object中,而不是Thread中 Object中的wait(), notify()等函式,和synchronized一樣,會對“物件的同步鎖”進行操作。

wait()會使“當前執行緒”等待,因為執行緒進入等待狀態,所以執行緒應該釋放它鎖持有的“同步鎖”,否則其它執行緒獲取不到該“同步鎖”而無法執行! OK,執行緒呼叫wait()之後,會釋放它鎖持有的“同步鎖”;而且,根據前面的介紹,我們知道:等待執行緒可以被notify()或notifyAll()喚醒。現在,請思考一個問題:notify()是依據什麼喚醒等待執行緒的?或者說,wait()等待執行緒和notify()之間是通過什麼關聯起來的?答案是:依據“物件的同步鎖”。

負責喚醒等待執行緒的那個執行緒(我們稱為“喚醒執行緒”),它只有在獲取“該物件的同步鎖”(這裡的同步鎖必須和等待執行緒的同步鎖是同一個),並且呼叫notify()或notifyAll()方法之後,才能喚醒等待執行緒。雖然,等待執行緒被喚醒;但是,它不能立刻執行,因為喚醒執行緒還持有“該物件的同步鎖”。必須等到喚醒執行緒釋放了“物件的同步鎖”之後,等待執行緒才能獲取到“物件的同步鎖”進而繼續執行。

總之,notify(), wait()依賴於“同步鎖”,而“同步鎖”是物件鎖持有,並且每個物件有且僅有一個!這就是為什麼notify(), wait()等函式定義在Object類,而不是Thread類中的原因。

  1. yield()介紹 yield()的作用是讓步。它能讓當前執行緒由“執行狀態”進入到“就緒狀態”,從而讓其它具有相同優先順序的等待執行緒獲取執行權;但是,並不能保證在當前執行緒呼叫yield()之後,其它具有相同優先順序的執行緒就一定能獲得執行權;也有可能是當前執行緒又進入到“執行狀態”繼續執行!

  2. yield() 與 wait()的比較 我們知道,wait()的作用是讓當前執行緒由“執行狀態”進入“等待(阻塞)狀態”的同時,也會釋放同步鎖。而yield()的作用是讓步,它也會讓當前執行緒離開“執行狀態”。它們的區別是: (01) wait()是讓執行緒由“執行狀態”進入到“等待(阻塞)狀態”,而不yield()是讓執行緒由“執行狀態”進入到“就緒狀態”。 (02) wait()是會執行緒釋放它所持有物件的同步鎖,而yield()方法不會釋放鎖。

    public class YieldLockTest{ private static Object obj = new Object();

    public static void main(String[] args){ ThreadA t1 = new ThreadA("t1"); ThreadA t2 = new ThreadA("t2"); t1.start(); t2.start(); }

    static class ThreadA extends Thread{ public ThreadA(String name){ super(name); } public void run(){ // 獲取obj物件的同步鎖 synchronized (obj) { for(int i=0; i <10; i++){ System.out.printf("%s [%d]:%d\n", this.getName(), this.getPriority(), i); // i整除4時,呼叫yield if (i%4 == 0) Thread.yield(); } } } } } (某一次)執行結果:

複製程式碼 t1 [5]:0 t1 [5]:1 t1 [5]:2 t1 [5]:3 t1 [5]:4 t1 [5]:5 t1 [5]:6 t1 [5]:7 t1 [5]:8 t1 [5]:9 t2 [5]:0 t2 [5]:1 t2 [5]:2 t2 [5]:3 t2 [5]:4 t2 [5]:5 t2 [5]:6 t2 [5]:7 t2 [5]:8 t2 [5]:9 `` 結果說明: 主執行緒main中啟動了兩個執行緒t1和t2。t1和t2在run()會引用同一個物件的同步鎖,即synchronized(obj)。在t1執行過程中,雖然它會呼叫Thread.yield();但是,t2是不會獲取cpu執行權的。因為,t1並沒有釋放“obj所持有的同步鎖”!

  1. sleep()介紹 sleep() 定義在Thread.java中。 sleep() 的作用是讓當前執行緒休眠,即當前執行緒會從“執行狀態”進入到“休眠(阻塞)狀態”。sleep()會指定休眠時間,執行緒休眠的時間會大於/等於該休眠時間;線上程重新被喚醒時,它會由“阻塞狀態”變成“就緒狀態”,從而等待cpu的排程執行。

  2. sleep() 與 wait()的比較 我們知道,wait()的作用是讓當前執行緒由“執行狀態”進入“等待(阻塞)狀態”的同時,也會釋放同步鎖。而sleep()的作用是也是讓當前執行緒由“執行狀態”進入到“休眠(阻塞)狀態”。 但是,wait()會釋放物件的同步鎖,而sleep()則不會釋放鎖。 下面通過示例演示sleep()是不會釋放鎖的。

複製程式碼 // SleepLockTest.java的原始碼 public class SleepLockTest{

private static Object obj = new Object();

public static void main(String[] args){ 
    ThreadA t1 = new ThreadA("t1"); 
    ThreadA t2 = new ThreadA("t2"); 
    t1.start(); 
    t2.start();
} 

static class ThreadA extends Thread{
    public ThreadA(String name){ 
        super(name); 
    } 
    public void run(){ 
        // 獲取obj物件的同步鎖
        synchronized (obj) {
            try {
                for(int i=0; i <10; i++){ 
                    System.out.printf("%s: %d\n", this.getName(), i); 
                    // i能被4整除時,休眠100毫秒
                    if (i%4 == 0)
                        Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } 
} 
複製程式碼

} 複製程式碼 主執行緒main中啟動了兩個執行緒t1和t2。t1和t2在run()會引用同一個物件的同步鎖,即synchronized(obj)。在t1執行過程中,雖然它會呼叫Thread.sleep(100);但是,t2是不會獲取cpu執行權的。因為,t1並沒有釋放“obj所持有的同步鎖”! 注意,若我們註釋掉synchronized (obj)後再次執行該程式,t1和t2是可以相互切換的。

相關文章