在代碼中調用 LockSupport.park() 時,會阻塞當前線程的執行。
AtomicBoolean status = new AtomicBoolean(false);
Thread waiter = new Thread(()->{
while (!status.get()) {
LockSupport.park();
}
});
status.compareAndSet(false, true);
LockSupport.unpark(waiter);
- 當其他線程調用 waiter.interrupt() 時,park() 方法會返回,但不會拋出異常,所以不需要捕獲異常;
- 當其他線程調用 LockSupport.unpark(waiter) 時,會爲 waiter 線程發放 permit ,park() 方法會返回;
- 除此之外,park() 方法也隨時有可能因 “no reason” 返回,因此必須在循環中判斷真實的等待條件是否已經滿足,否則應該繼續調用 park();
被阻塞的線程是無法知道 park() 方法到底是因爲哪種原因而返回的,需要額外的手段去做輔助判斷。
因此,park() 只是提供了一種讓線程阻塞的方式,可以基於它創建 high level 的同步機制,比如鎖;代碼結構應該類似於以下形式:
while (!canProceed()) { ... LockSupport.park(this); }}
不管 canProceed() 條件是在 park() 被調用之前或之後成立,都不影響最終的結果,因爲 park() 是否返回是基於 permit,而不是 unpark 的調用時機。
使用 LockSupport 實現先入先出的不可重入鎖,示例代碼:
class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters
= new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
// Block while not first in queue or cannot acquire lock
while (waiters.peek() != current ||
!locked.compareAndSet(false, true)) {
LockSupport.park(this);
if (Thread.interrupted()) // ignore interrupts while waiting
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted) // reassert interrupt status on exit
current.interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
調用 park 時,可以攜帶一個 blocker 參數,該參數通常用來保存線程阻塞的觸發對象,比如上例中的 FIFOMutex 對象,其他線程可以通過 getBlocker( waiter) 方法來獲取 blocker 對象。