曾經面試被問到JAVA中實現線程掛起和喚醒有哪些方式,除了採用Object類中wait notify/notifyAll實現還有其他方式嗎?當時真心沒答上來,後來做了功課才知道還有LockSupport類中park/unpark可以實現。
大家如果瞭解AQS(全稱AbstractQueuedSynchronizer),就知道併發編程中很多類都是基於AQS實現的,例如ReentrantLock、 CountDownLatch、Semaphore等等。
通過分析AQS源碼,不難發現AbstractQueuedSynchronizer中ConditionObject內部類就採用LockSupport實現線程掛起和喚醒。
例如ConditionObject中await方法如下:
例如ConditionObject中signal方法如下:
ConditionObject中signal方法調用doSignal方法如下:
ConditionObject中doSignal調用外部類的transferForSignal方法如下:
我們可以看出LockSupport類中park/unpark同樣可以實現線程掛起和喚醒。其實如果大家看過我之前寫的‘手寫一個同步類容器(互聯網大廠面試題目)’文章,就知道文章有采用Lock/Condition await signalAll實現 更精確定位喚起的線程,它的底層實現就是LockSupport的park/unpark。
我們來分析LockSupport源碼:
通過LockSupport源碼設計我們知道它實現線程掛起和喚醒是基於Unsafe底層實現。代碼不是很複雜,我們這裏不做詳細
講解。
我們來找到Unsafe中park、unpark源碼:
由此,我們就完整的跟進了LockSupport類中park/unpark的設計理念。
現在我們通過一段簡單的代碼,來初步理解LockSupport如何掛起和喚醒線程。
package com.locksupport;
import java.util.concurrent.locks.LockSupport;
/**
* LockSupport源碼分析、使用場景說明
*
* @author 小輝GE/小輝哥
* <p>
* 2019年8月30日 下午21:30:00
*/
public class LockSupportAnalysis {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
System.out.println("begin......");
// 當前線程wait
LockSupport.park();
System.out.println("end......");
});
thread.start();
// Thread.sleep(1000);
// 線程解除wait
LockSupport.unpark(thread);
}
}
測試輸出結果如下:
結果分析:
- LockSupport不需要在同步代碼塊裏。所以線程間也不需要維護一個共享的同步對象了,從而降低了使用時複雜度。
- unpark函數可以優先於park調用,所以不需要擔心線程間的執行先後順序。
以上代碼僅供參考,如有不當之處,歡迎指出!!!
更多幹貨,歡迎大家關注和聯繫我。期待和大家一起更好的交流、探討技術!!!