執行緒的幾種狀態

塵虛緣_KY發表於2017-05-07

目錄

執行緒狀態

方法的比較

問題與解答


此節主要看以下執行緒的狀態,通過原始碼的列舉型別,我們可以看到執行緒的六種狀態。

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

執行緒狀態

此六種狀態的轉換關係如下圖所示: 

圖一:執行緒狀態流轉示意圖

1. 初始狀態-NEW
  當我們通過實現Runnable介面和繼承Thread建立一個執行緒類,new一個例項出來,執行緒就進入了初始狀態。

Thread thread = new Thread(); //初始狀態;

2. 執行狀態-RUNNABLE
  當被排程程式選中後,執行緒就獲取了CPU的執行許可權,獲取了CPU執行時間分片後,進行執行執行階段
ps:就緒狀態-READY
     就緒狀態其實是一個不存在的狀態,但是為了好理解,我們加入了這樣一個狀態階段,就緒狀態只是說你有資格參與競爭鎖和獲取鎖,一切準備妥當,隨時等待著被呼叫執行。就緒狀態包含以下情況:

  • 執行緒呼叫了start()方法;
  • 當前執行緒sleep()方法結束,其他執行緒join()結束,I/O等待結束,例如使用者輸入完畢;
  • 拿到某個執行緒拿到物件鎖;
  • 當前執行緒時間片用完了,呼叫當前執行緒的yield()方法,當前執行緒進入就緒狀態。

3. 阻塞狀態 BLOCKED
    阻塞狀態是執行緒需要執行同步程式碼塊synchronized關鍵字修飾的方法或程式碼塊,為了獲取鎖而阻塞的狀態;
4. 等待 WAITTING
    此狀態的執行緒將會處於無限期的等待狀態,不會被分配CPU執行時間,直到它們被notify()/notifyAll()/LockSupport.unpark()顯式地喚醒;
5. 超時等待 TIMED_WAITTING
    此狀態的執行緒不會被分配CPU執行時間,但是它們不必無限期等待被其他執行緒顯示地喚醒,在達到time_out時間達後就會自動喚醒。
6. 終止狀態 TERMINATED
     當執行緒的run()方法完成時,或者主執行緒的main()方法完成時,我們就認為它終止了。執行緒一旦終止了,就不能復生。
在一個終止的執行緒上呼叫start()方法,會丟擲java.lang.IllegalThreadStateException異常。

ps:物件鎖和類鎖
   這兩種鎖是不同的鎖,所以多個執行緒同時執行這2個不同鎖的方法時,是非同步的。
synchronized 加到 static 方法前面是給class 加鎖,即類鎖,對所有的例項物件都起作用;
而synchronized 加到非靜態方法前面是給物件上鎖。

方法的比較

Thread.sleep(long millis)

   當前執行緒呼叫此方法,即當前執行緒會從“執行狀態”進入到“休眠(阻塞)狀態”-TIMED_WAITING狀態,但不釋放物件鎖,millis後執行緒自動甦醒進入就緒狀態。
   作用:自身等待,給其它執行緒執行的機會;

Thread.yield()

   當前執行緒呼叫此方法,當前執行緒由“執行狀態”變為“就緒狀態”,當前執行緒放棄獲取的CPU時間片,但不釋放鎖資源,讓OS再次選擇執行緒。
作用:讓相同優先順序的執行緒輪流執行,但並不保證一定會輪流執行。實際中並不能保證在當前執行緒呼叫yield()之後,其它具有相同優先順序的執行緒就一定能獲得執行權;也有可能是當前執行緒又進入到“執行狀態”繼續執行。
該方法與sleep()類似,只是不能由使用者指定暫停多長時間。

thread.join()/thread.join(long millis)

   當前執行緒裡呼叫其它執行緒t的join方法,當前執行緒由“執行狀態”變為“等待狀態”-WAITING/TIMED_WAITING狀態,當前執行緒不會釋放已經持有的物件鎖。執行緒t執行完畢或者millis時間到,當前執行緒一般情況下進入RUNNABLE狀態;因為join是基於wait實現的,如果沒有獲取到synchronized鎖,將會進入進入BLOCKED狀態。

obj.wait()/wait(long timeout)

   當前執行緒呼叫物件的object.wait(),當前執行緒由“執行狀態”進入到“等待(阻塞)狀態”釋放物件監視器(物件鎖)的所有權,進入等待佇列,依靠notify()/notifyAll()喚醒或者timeout時間到自動喚醒,然後同步佇列裡競爭鎖,只有同步佇列的執行緒才有資格獲取物件鎖,只有噹噹前執行緒獲取該物件監視器後才可以繼續執行。

obj.notify()/notifyAll()

  喚醒在此物件監視器上等待的單個執行緒,由“等待佇列”進入“同步佇列”,喚醒的執行緒是任意的。notifyAll()喚醒在此物件監視器上等待的所有執行緒。

LockSupport.park()/LockSupport.parkNanos(long nanos)/LockSupport.parkUntil(long deadlines)

當前執行緒由“執行狀態”進入“等待狀態”-WAITING/TIMED_WAITING狀態。
LockSupport對比Object的wait/notify有兩大優勢:
(1) LockSupport不需要在同步程式碼塊裡 。所以執行緒間也不需要維護一個共享的同步物件了,實現了執行緒間的解耦
(2) unpark函式可以先於park呼叫,所以不需要擔心執行緒間的執行的先後順序。

問題與解答

問題1:為什麼wait和notify方法是Object類中的方法,而不是在Thread裡?

  因為:執行緒與鎖是分不開的,執行緒的同步、等待、喚醒都與物件鎖是密不可分的。notify(), wait()依賴於“同步鎖”,而“同步鎖”是物件鎖持有,依賴於物件,並且每個物件有且僅有一個,這就是為什麼notify(), wait()等函式定義在Object類。

問題2:為什麼wait和notify需要配合synchronized使用?

    /**
     * Causes the current thread to wait until either another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object, or a
     * specified amount of time has elapsed.
     * <p>
     * The current thread must own this object's monitor. //必須獲取物件監視器
     * <p>
    **/
public final native void wait(long timeout) throws InterruptedException;

    通過jdk的原始碼可以發現,雖然wait()是native方法,但是註釋上有一句話:“The current thread must own this object's monitor.”,所以就是使用wait()必須擁有物件的監視器monitor,我們知道監視器是synchronized底層的實現,synchronized的底層使用了monitorenter和monitorexit指令,不進入程式碼塊是無法擁有這個物件的monitor的。所以必須配合synchronized使用。
如果wait和notify不配合synchronized使用,就會丟擲 java.lang.IllegalMonitorStateException 的異常。

問題3: Thread wait()和sleep()的區別?

所屬的類 sleep是屬於Thread類的方法;
wait()是屬於Object類的方法;
用法

(1) sleep必須指定時間;(2) sleep可以在任意的地方使用;
(1) wait可以不用指定時間;(2) wait只能在同步程式碼塊和方法中使用,必須配合synchronized使用,否則報錯;

釋放資源 sleep釋放cpu的執行許可權,但是不釋放物件鎖許可權;
wait釋放cpu的執行權和物件鎖許可權;

問題4: yield() 與 wait()的比較

狀態轉換

wait()是讓執行緒由“執行狀態”進入到“等待(阻塞)狀態”;

yield()是讓執行緒由“執行狀態”進入到“就緒狀態”;

資源釋放

wait()是會執行緒釋放它所持有物件的同步鎖;

yield()方法不會釋放鎖。

 

參考資料

https://blog.csdn.net/WuLex/article/details/78992692
https://blog.csdn.net/pange1991/article/details/53860651
https://www.cnblogs.com/happy-coder/p/6587092.html
https://www.cnblogs.com/GooPolaris/p/8079490.html
https://blog.csdn.net/qq_32924343/article/details/79914175
https://www.cnblogs.com/qingquanzi/p/8228422.html
https://blog.csdn.net/qq_32924343/article/details/79914175

 

 

相關文章