LockSupport 中的 park 與 unpark

在代碼中調用 LockSupport.park() 時,會阻塞當前線程的執行。

        AtomicBoolean status = new AtomicBoolean(false);
        Thread waiter = new Thread(()->{
            while (!status.get()) {
                LockSupport.park();
            }
        });

        status.compareAndSet(false, true);
        LockSupport.unpark(waiter);
  1. 當其他線程調用 waiter.interrupt() 時,park() 方法會返回,但不會拋出異常,所以不需要捕獲異常;
  2. 當其他線程調用 LockSupport.unpark(waiter) 時,會爲 waiter 線程發放 permit ,park() 方法會返回;
  3. 除此之外,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 對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章