AbstractQueuedSynchronizer是一个同步架构, Java中有很多同步类是由这个架构实现,比如FutureTask、ReentrantLock等。为了使用这个类实现一个同步架构,需要将其定义为一个帮助子类,需要适当地重新定义以下方法,这是通过使用getState 、 setState、或compareAndSetState 方法来检查和/或修改同步状态来实现的:
您也可以查找从 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;(共享模式与此类似,但可能涉及级联信号。)
因为要在加入队列之前检查线程的获取状况,所以新获取的线程可能闯入 其他被阻塞的和已加入队列的线程之前。不过如果需要,可以内部调用一个或多个检查方法,通过定义 tryAcquire 和/或 tryAcquireShared 来禁用闯入。特别是 getFirstQueuedThread()
没有返回当前线程的时候,严格的 FIFO 锁定可以定义 tryAcquire 立即返回 false。只有 hasQueuedThreads()
返回 true 并且 getFirstQueuedThread 不是当前线程时,更好的非严格公平的版本才可能会立即返回 false;如果 getFirstQueuedThread 不为 null 并且不是当前线程,则产生的结果相同。出现进一步的变体也是有可能的。
对于默认闯入(也称为 greedy、renouncement 和 convoy-avoidance)策略,吞吐量和可伸缩性通常是最高的。尽管无法保证这是公平的或是无偏向的,但允许更早加入队列的线程先于更迟加入队列的线程再次争用资源,并且相对于传入的线程,每个参与再争用的线程都有平等的成功机会。此外,尽管从一般意义上说,获取并非“自旋”,它们可以在阻塞之前对用其他计算所使用的 tryAcquire 执行多次调用。在只保持独占同步时,这为自旋提供了最大的好处,但不是这种情况时,也不会带来最大的负担。如果需要这样做,那么可以使用“快速路径”检查来先行调用 acquire 方法,以这种方式扩充这一点,如果可能不需要争用同步器,则只能通过预先检查 hasContended()
和/或 hasQueuedThreads()
来确认这一点。
通过特殊化其同步器的使用范围,此类为部分同步化提供了一个有效且可伸缩的基础,同步器可以依赖于 int 型的 state、acquire 和 release 参数,以及一个内部的 FIFO 等待队列。这些还不够的时候,可以使用 atomic
类、自己的定制 Queue
类和 LockSupport
阻塞支持,从更低级别构建同步器。
使用示例
以下是一个非再进入的互斥锁类,它使用值 0 表示未锁定状态,使用 1 表示锁定状态。当非重入锁定不严格地需要当前拥有者线程的记录时,此类使得使用监视器更加方便。它还支持一些条件并公开了一个检测方法:
class Mutex implements Lock, java.io.Serializable { // Our internal helper class private static class Sync extends AbstractQueuedSynchronizer { // Report whether in locked state protected boolean isHeldExclusively() { return getState() == 1; } // Acquire 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; } // Release 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; } // Provide a Condition Condition newCondition() { return new ConditionObject(); } // Deserialize 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)); } }看公有方法,是我们所熟悉的几个同步类的方法,他们封装了sync的方法。
1. lock(), 这个方法调用了sync.acquire(1)获取了独占锁acquire的定义如下
public final void acquire(int arg)
- 以独占模式获取对象,忽略中断。通过至少调用一次 tryAcquire(int) 来实现此方法,并在成功时返回。否则在成功之前,一直调用 tryAcquire(int) 将线程加入队列,线程可能重复被阻塞或不被阻塞。可以使用此方法来实现 Lock.lock() 方法。
-
-
-
参数:
- arg - acquire 参数。此值被传送给 tryAcquire(int),但它是不间断的,并且可以表示任何内容。
我们看到acquire是通过调用tryAcquire(int)来实现的,直到成功返回时结束,因此我们无需要自定义这个方法就可用它来实现lock。
2.tryLock()是由sync.tryAcquire(1)来实现
3.unlock()由Release(int)来实现,我们看下release的定义
public final boolean release(int arg)
- 以独占模式释放对象。如果 tryRelease(int) 返回 true,则通过消除一个或多个线程的阻塞来实现此方法。可以使用此方法来实现 Lock.unlock() 方法
-
-
-
参数:
- arg - release 参数。此值被传送给 tryRelease(int),但它是不间断的,并且可以表示任何内容。 返回:
- 从 tryRelease(int) 返回的值
这个类似与tryAcquire(int)
剩下方法分别调用了AQS的其他方法,不再一一介绍,下面看下tryAcquire(int)方法的实现
// Acquire 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; }
我们看到首先去compareAndSetState(0, 1),如果满足,则setExclusiveOwnerThread(Thread.currentThread()),这个方法是AQS的父类
AbstractOwnableSynchronizer的方法,这是个作为监控作用的类。之后成功以后,返回true,否则返回false。
总结
当类库提供的同步类不能满足我们的要求的时候,或者我们需要关注类实现细节的时候,我们需要对AQS类有所了解,这是个同步框架,用来实现基于状态的同步类,其中最关键的方法是getState()、setState()、compareAndSetState(),这三个方法由架构提供,我们需要调用这三个方法来实现tryAcquire等方法来建立我们自定义的同步类,当然在一般情况下类库提供的同步类已足够我们所使用。