04 JAVA中park/unpark的原理以及JAVA在API層面線程狀態總結


1 park與unpark的使用以及原理

1-1 基本使用

  • park/unpark並非線程類的方法,是concurrent的方法
// 暫停當前線程
LockSupport.park();
// 恢復某個線程的運行
LockSupport.unpark(暫停線程對象)

實例

package chapter4;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.LockSupport;
import chapter2.Sleeper;
import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.test3")
public class test3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("start...");
            try {
                Thread.sleep(1000);                // 睡眠時間1
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.warn("park...");
            LockSupport.park();
            log.warn("resume...");
        },"t1");
        t1.start();
        Thread.sleep(2000);
        log.warn("unpark...");                      //睡眠時間2
        LockSupport.unpark(t1);
    }
}

運行結果1(設置睡眠時間1為1000ms,睡眠時間2為2000ms,即unpark在park之后執行

[t1] WARN c.test3 - park...
[main] WARN c.test3 - unpark...
[t1] WARN c.test3 - resume...

運行結果2(設置睡眠時間1為2000ms,睡眠時間2為1000ms,即park在unpark之后執行

[main] WARN c.test3 - unpark...
[t1] WARN c.test3 - park...
[t1] WARN c.test3 - resume...

總結:上面的2個結果說明,park與unpark成對使用時,對使用前后的次序並不敏感。原因見原理部分。

1-2 park/unpark與wait/notify的區別

  • wait/notify必須在有鎖的情況下使用(需要關聯Monitor對象),park/unpark沒有這個限制條件。
  • park/unpark配對使用能夠精確的指定具體的線程的阻塞/運行,notify只能隨機喚醒一個線程
  • park/unpark配對使用可以先unpark,wait/notify配合使用不能夠先notify

1-3 park/unpark的底層原理

1-3-1 先park后unpark的場景分析

step1: 線程0在執行的過程中調用park方法。

step2:檢查_counter是否為0

  • 為0,獲得_mutex互斥鎖
  • 為1,說明之前其他線程調用過park方法,則將_counter設為1后線程繼續執行。(先unpark后park的場景

step3:獲得互斥鎖之后,線程進入 _cond 條件變量阻塞

step4: 某線程在執行的過程中調用unpark方法后,設置_counter為1。

step5:喚醒 _cond 條件變量中的 Thread_0

step6:Thread_0 恢復運行 ,並恢復_counter為0。

1-3-2 先unpark后park的場景分析

step1: 某線程調用unpark方法后,_counter被設置為1。

step2:線程0執行過程中調用park方法,檢查_counter是1,無法獲得互斥變量__mutex進入阻塞隊列

step3:線程0恢復_counter為0並繼續執行。

總結

從2個例子中可以看到,park方法調用后,必須滿足_counter為0,才能進入阻塞隊列。如果在park之間調用unpark,那么park方法就會失效,無法讓線程停止運行。

2 JAVA中API層面的線程10種狀態轉換

2-1 六種狀態回顧

NEW:線程剛剛被創建時,還沒有start()的狀態

RUNABLE: Java中的RUNABLE包含了操作系統層面的運行,阻塞,可運行狀態。

  • 操作系統層面的線程的運行,阻塞等在Java層面無法體現出來

BLOCKED,WAITING,TIMED_WAITINGJava API層面的阻塞

  • TIMED_WAITING:使用sleep方法可能會出現
  • WAITING: 使用join方法后可能會出現
  • BLOCKED:使用synchronize方法可能會出現

2-2 狀態的轉換10種情況分析

假設有線程t。

情況1 NEW --> RUNNABLE

  • 線程t被定義后,其狀態為NEW,當調用 t.start() 方法時,由 NEW --> RUNNABLE

情況2,3,4 RUNNABLE <--> WAITING

wait/notify|wait/interrupt
  • 線程t何時從RUNNABLE變為WAITING?
    • t在執行過程中通過synchroized(obj)獲取到鎖(擁有了monitor的owner),然后調用obj.wait()方法,則線程進入WAITING狀態(進入到monitor的waitset等待)。
  • 線程t何時從WAITING變為RUNABLE?
    • 調用 obj.notify() , obj.notifyAll() , t.interrupt() 時(notify或者使用中斷)。
      • 競爭鎖成功,t 線程從 WAITING --> RUNNABLE
      • 競爭鎖失敗,t 線程從 WAITING --> BLOCKED (進入到monitor的entryList等待)
join|join/interrupt(可以將“當前線程”作為“主線程”)
  • 當前線程何時從RUNNABLE變為WAITING?
    • 當前線程(比如主線程)需要等待t線程運行結束,調用join()的時候(此時調用線程會進入到線程t關聯的monitor中的waitset進行等待)。
  • 當前線程何時從WAITING變為RUNABLE?
    • t 線程運行結束 (猜測:線程t運行結束,其作為鎖對象關聯的monitor被回收,在monitor上等待的當前線程不會再等待又繼續執行)
    • t線程調用當前線程的interrupt方法
park/unpark| park/interrupt(可以將當前線程作為主線程)
  • 當前線程何時從RUNNABLE變為WAITING?

    • 當前線程調用 LockSupport.park() 方法
  • 當前線程何時從WAITING變為RUNABLE?‘

    • t線程調用了LockSupport.unpark(目標線程)
    • t線程調用了當前線程的interrupt方法

情況5,6,7,8 RUNNABLE <--> TIMED_WAITING(結合情況2,3,4看)

wait(n)/notify|wait(n)調用/等待足夠時間|wait(n)/interrupt
join(n)/interrupt|join(n)調用后/等待足夠時間
sleep(n)/等待足夠時間
parkNanos(nanos),parkUntil(millis) /unpark(目標線程) 或者interrupt或者等待超時

情況9 RUNNABLE <--> BLOCKED

  • t線程何時從 RUNNABLE變為BLOCKED?

    • t 線程用 synchronized(obj) 嘗試獲取對象鎖時但是競爭失敗 (此時線程t會進入對象頭的阻塞隊列
  • 當前線程何時從BLOCKED變為RUNNABLE?‘

    • 持 obj 鎖線程的同步代碼塊執行完畢,會喚醒該對象上所有 BLOCKED 的線程重新競爭,其中 t 線程競爭成功。

情況 10 RUNNABLE <--> TERMINATED

當前線程所有代碼運行完畢,進入 TERMINATED


2-3 WAITING與BLOCKED的區別(從圖中分辨)

總結:從上面的圖可以看出WAITING線程與BLOCKED線程在不同地方。WAITING線程在對象頭的WaitSet,而BLOCKED線程在對象頭的EntryList

  • waiting:主動為之,wait()方法釋放cpu執行權和釋放鎖進入對象頭的Waitset ,需要notify()喚醒,然后進入到EntryList等待競爭鎖。

  • blocked:被動的,線程在競爭鎖的時候失敗,被阻塞,進入EntryList。


參考資料

並發編程課程

java線程中timed和blocked兩種狀態的區別


20210303


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM