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 对象。

你可能感兴趣的:(Java)