LockSupport淺析


最初想有沒有必要寫這類文章,網上相關的文章很多,有些更為透徹,自己再寫一篇不免有重復造輪子的感覺。
但想想寫文除了分享知識外也可以幫助自己總結歸納,也稍稍可以提高點自我滿足感。


基本的線程阻塞原語,被用於創建鎖和其他同步類上。

這個類的作用有點類似於Semaphore,通過許可證(permit)來聯系使用它的線程。如果許可證可用,調用park方法會立即返回並在這個過程中消費這個許可,不然線程會阻塞。調用unpark會使許可證可用。(和Semaphores有些許區別,許可證不會累加,最多只有一張)

因為有了許可證,所以調用parkunpark的先后關系就不重要了,這里可以對比一下Object的waitnotify,如果先調用同一個對象的notifywait,那么調用wait的線程依舊會被阻塞,依賴方法的調用順序。在一些場景下,LockSupport的方法都可以取代notifywait。舉個簡單的例子:

final Object lock = new Object();
Thread t1 = new Thread() {
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (lock) {
            lock.notifyAll();
        }
    };
};

Thread t2 = new Thread() {
    public void run() {
        synchronized (lock) {
            try {
                lock.wait();
                System.out.println("I am alive");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
};
t1.start();
t2.start();

這段代碼,只有確保t2先進入臨界區並執行wait后才能正常打印內容。
換成LockSupport就沒有這種擔心了。

Thread t2 = new Thread() {
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t2 end sleep");
        LockSupport.park(this);
        System.out.println("I am alive");
    };
};

Thread t1 = new Thread() {
    public void run() {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LockSupport.unpark(t2);
        System.out.println("I am done");
    };
};
t1.start();
t2.start();

可以先unparkpark,但如API文檔所說,unpark需要傳入的線程已經啟動。在上述t1.start()后用t1.join()等方法在t2啟動前調用了unpark就無效了。

但也不意味着wait,notify就沒用了。wait是調用對象的方法,和這個對象資源綁定,線程間不需要彼此感知到對方(能獲取對方引用),只需要共有這個資源即可,另一方面LockSupport可以指定unpark的線程,這點也是前者無法做到的,兩者間不能完全取代彼此。

park方法也有可能在沒有"任何理由"的情況下返回,所以通常要在一個循環中使用它並重新判斷可以返回的條件。這一點和Object.wait方法一樣,可以參考wiki,大致可以理解為底層系統因為優化需要允許出現這樣的情況,JVM的線程就是系統線程這種情況也無法避免。可以作為"忙等待"的優化,但是必須要注意要有配對的unpark方法。
使用模式如下:

while (!canProceed()) { ... LockSupport.park(this); }}  

在提供的方法中可以看到有一組傳入名為blocker對象作為參數的方法,記錄這個對象可以方便許可證監視和診斷工具分析。

取上述代碼,不啟動t1的情況直接運行t2進入阻塞,jstack可以看到:

"Thread-0" #10 prio=5 os_prio=0 tid=0x000000001b586800 nid=0x37f0 waiting on condition [0x000000001c0ae000]
java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000d603a6e8> (a test.TestLockSupport$1)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at test.TestLockSupport$1.run(TestLockSupport.java:21)  

會將傳入的對象打出。


更多資料:
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/LockSupport.html

http://agapple.iteye.com/blog/970055

http://blog.csdn.net/hengyunabc/article/details/28126139


免責聲明!

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



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