AbstractQueuedSynchronizer

255 阅读6分钟

java.util.concurrent.locks 
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable

提供了一个框架去实现基于FIFO等待队列的阻塞锁和相关的同步器(semaphore,event等)。这个类被设计为一个适用于大多数依赖一个单独原子int值去代表状态的同步器。子类必须明确(覆写,重写)那些改变state的protected方法,并定义state对于正在获取或释放对象

的含义。鉴于此,这个类的其他方法构建了排队和阻塞的机制。字段可以维护其他state字段,但是只跟踪使用getState、setState和compareAndSetState方法操作的原子更新的int值的同步。

子类应该被定义为非公共内部帮组类,以被用于实现实现外部类同步属性。AQS类没有实现任何同步接口。取而代之的是定义了一些类似acquireInterruptibly的方法,能够在适当的时候被具体的锁和相关的同步器调用,以实现他们的公共方法。

这个类支持一个或两个默认的独占和共享模式。当以独占模式请求,其他线程尝试请求不会成功。共享模式下,多线程的请求也许可以(但是也可以不需要)成功。这个类不会“理解”这些不同,除了在机械感觉下,即当一个共享模式的请求成功了,下一个等待的线程(假设存在)必须也明确是否它也能请求。在不同模式下的线程等待共享一个同样的FIFO队列。通常地,实现的子类只支持这些模式中的一种。但是也有两种模式多发挥作用的,例如在ReadWriteLock中。只支持独占模式或共享模式的子类不需要实现那些支持未使用模式的方法。

这个类定义了一个 AbstractQueuedSynchronizer.ConditionObject的内部类,可以用作Condition由子类实现支持独占模式的方法isHeldExclusively报告同步是否只对当前线程持有,方法release与当前调用getState的值完全释放该对象,并acquire,鉴于这个保存的状态值,最终获得的该对象恢复到以前的状态。否则没有方法去创建这样一个condition,因此如果这个约束没有被满足,就不要使用它。AbstractQueuedSynchronizer.ConditionObject的行为当然基于它的同步器实现的语义。

这个类为内部队列提供了检查,仪表化和监控方法,同样也为condition object提供了相似的方法。可以根据需要使用AQS导出到类中,以实现他们的同步机制。

这个类的序列化只存储底层以原子整形维护的state,因此反序列化的对象只有空的线程队列。通常子类请求序列化将会定义一个readObject方法,以还原这个子类到一个已知的初始化state在反序列化后。

使用

为了使用这个类作为一个同步器的基础,重新定义(重写)下列方法,在合适的时候,使用getState, setState 和/或 compareAndSetState方法,通过检查和/或修改同步状态:

  • tryAcquire
  • tryRelease 
  • tryAcquireShared
  • tryReleaseShared 
  • isHeldExclusively
这些方法的默认实现都是抛出UnsupportedOperationException。这些方法的实现必须内部线程安全,并且通常来说应该短暂(执行)和非阻塞。定义这些方法是使用这个类的唯一途径。其他所有的方法都被声明为final,因为它们不能独立地更改。 


你也可以从 AbstractOwnableSynchronizer找到继承的方法,(这些方法)对线程拥有的一个独占同步器的追踪非常有用。我们鼓励你去使用它们 -- 这将使监控和诊断工具能够帮助使用者明确哪个线程持有锁。

尽管这个类是基于一个内部的FIFO队列,(但是)它没有自动的执行FIFO的获取策略。独占模式同步的核心形式如下:

   Acquire:
       while (!tryAcquire(arg)) {
          enqueue thread if it is not already queued;
          possibly block current thread;
       }
  
   Release:
       if (tryRelease(arg))
          unblock the first queued thread;

(共享模式也是类似的,只是可能涉及到级联的信号)

因为在排队之前有一个acquire的检查可以被调用,(那么)一个新的请求线程可能抢在那些阻塞和排队线程的前面。然而,如果需要的话,你可以定义tryAcquire 和/或 tryAcquireShared方法取消抢占,通过调用内部一个或多个检查方法,从而,就提供了一个公平FIFO请求规则。特别是,大多数公平同步器可以定义 tryAcquire返回false,如果hasQueuedPredecessors(一个特地设计用于公平同步器的方法)返回true。其他的变化都是可能的。

吞吐量和可扩展性对于默认的抢占(也被称为:饥饿,拒绝和护送-废止)模式来说是最高的。当不能保证是公平或starvation-free(饥饿-自由)的时候,更早排队的线程被允许重新竞争,在更晚排队线程之前,并且每个重新的竞争都有一个公正的机会去对抗新进的线程。此外,虽然acquire不像通常意义上的“自旋”,但是也可能多次调用tryAcquire,以及穿插一些其他计算在阻塞之前。仅当独占同步是短暂的持有,自旋才能提供大多数好处,而当不是这种情况(短暂持有)下,也无需承担大多数的(资源消耗)责任。如果想要的话,你可以增加(资源消耗的责任),通过调用acquire方法之前使用“fast-path”检查,可能预检查 hasContended 和/或 hasQueuedThreads方法,仅当同步器看起来好像没有被竞争。

这个类在某种程度上提供了一个有效的和可扩展的基础对于同步器,通过专门化它的使用范围可以基于int state,acquire和release参数,和一个内部FIFO等待队列的同步器。当这样还没有满足,你可以通过一个使用较低级别atomic类构建一个同步器,你自己自定义 java.util.Queue类,和LockSupport阻塞支持。

使用范例

这是一个非可重入互斥锁类,使用0代表未锁定状态,使用1代表已锁定状态。虽然这个类不严格要求记录当前拥有锁的线程,但是这样是为了这个类更易用。它也支持conditions和暴露仪器方法中的一个:

 class Mutex implements Lock, java.io.Serializable {

   // Our internal helper class
   private static class Sync extends AbstractQueuedSynchronizer {
     // Reports whether in locked state
     protected boolean isHeldExclusively() {
       return getState() == 1;
     }

     // Acquires the lock if state is zero
     public boolean tryAcquire(int acquires) {
       assert acquires == 1; // Otherwise unused
       if (compareAndSetState(0, 1)) {
         setExclusiveOwnerThread(Thread.currentThread());
         return true;
       }
       return false;
     }

     // Releases the lock by setting state to zero
     protected boolean tryRelease(int releases) {
       assert releases == 1; // Otherwise unused
       if (getState() == 0) throw new IllegalMonitorStateException();
       setExclusiveOwnerThread(null);
       setState(0);
       return true;
     }

     // Provides a Condition
     Condition newCondition() { return new ConditionObject(); }

     // Deserializes properly
     private void readObject(ObjectInputStream s)
         throws IOException, ClassNotFoundException {
       s.defaultReadObject();
       setState(0); // reset to unlocked state
     }
   }

   // The sync object does all the hard work. We just forward to it.
   private final Sync sync = new Sync();

   public void lock()                { sync.acquire(1); }
   public boolean tryLock()          { return sync.tryAcquire(1); }
   public void unlock()              { sync.release(1); }
   public Condition newCondition()   { return sync.newCondition(); }
   public boolean isLocked()         { return sync.isHeldExclusively(); }
   public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
   public void lockInterruptibly() throws InterruptedException {
     sync.acquireInterruptibly(1);
   }
   public boolean tryLock(long timeout, TimeUnit unit)
       throws InterruptedException {
     return sync.tryAcquireNanos(1, unit.toNanos(timeout));
   }
 }

这是一个latch类,类似CountDownLatch,除了该类只需要一个单独的信号就能发射。因为latch是非独占的,它使用的是共享acquire和release方法。

 class BooleanLatch {

   private static class Sync extends AbstractQueuedSynchronizer {
     boolean isSignalled() { return getState() != 0; }

     protected int tryAcquireShared(int ignore) {
       return isSignalled() ? 1 : -1;
     }

     protected boolean tryReleaseShared(int ignore) {
       setState(1);
       return true;
     }
   }

   private final Sync sync = new Sync();
   public boolean isSignalled() { return sync.isSignalled(); }
   public void signal()         { sync.releaseShared(1); }
   public void await() throws InterruptedException {
     sync.acquireSharedInterruptibly(1);
   }
 }

since:1.5