稀土 2017-12-21
AQS(AbstractQueuedSynchronizer)是一个用于构建锁和同步器的框架,许多同步器都可以通过AQS很容易并且高效的构造出来。不仅Reentrant和Semaphore是基于AQS构建的,还包括CountDownLatch、ReentrantReadWriteLock、SynchronousQueue和FutureTask。
在基于AQS构建的同步器类中,最基本的操作包括各种形式的获取操作和释放操作。
获取操作是一种依赖状态的操作,并且通常会阻塞直到达到了理想状态。如:
释放并不是一个可阻塞的操作,当执行“释放”操作时,所有在请求时被阻塞的线程都会开始执行。
AQS提供了一个高效的状态机模型,用来管理同步器类中的状态和。
AQS使用一个整数state以表示状态,并通过getState、setState及compareAndSetState等protected类型方法进行状态转换。巧妙的使用state,可以表示任何状态,如:
除了state,在同步器类中还可以自行管理一些额外的状态变量。如:
状态转换则表现为不同的获取操作和释放操作,其标准形式如下:
boolean acquire () throws InterruptedException { while (当前状态不允许获取操作) { if (需要阻塞获取请求) { 如果当前线程不在队列中,则将其插入队列 阻塞当前新城 } else 返回失败 } 可能更新同步器的状态 如果当前线程在队列中,则将其移出队列 返回成功 } void release () { 更新同步器的状态 if (新的状态允许某个被阻塞的线程获取成功) 接触队列中一个或多个线程的阻塞状态 }
为什么10行只是“可能”,而不是“必然”更新同步器的状态呢?因为获取同步器的某个线程可能对其他线程能否也获取该同步器造成影响,也可能不影响。如使用独占的ReentrantLock时,一个线程获取锁后,其他线程就不能再获取锁,于是需要更新同步器的状态;但使用CountDownLatch时,一个线程获取闭锁时(包括正在获取和获取后),不会影响其他线程能否获取它,因此不需要更新同步器的状态。
根据是否支持阻塞、是否支持独占等,获取操作和释放操作都有多个实现。获取操作有acquire、acquireShared、tryAcquire、tryAcquireShared等,释放操作有release、releaseShared、tryRelease、tryReleaseShared等。
还有acquireNanos、acquireInterruptibly等实现,为了讲解方便,暂时忽略它们。
不带try前缀的方法是阻塞的(当然release、releaseShared不是可阻塞的),通过调用带try前缀的相应版本实现,如acquire内部调用tryAcquire并维护相关逻辑。AQS抽象类中提供了不带try前缀的方法,并以final修饰,在实现同步器时应直接使用;需要覆写的是带try前缀的方法。对于这些方法,约定通过返回值告知调用者(一般是AQS)获取或释放操作是否成功,一些特殊的值代表额外的信息:
要想基于AQS构建同步器,就必须对上述四个方法烂熟于心。
本文虽短,且非常重要,是后文分析ReentrantLock等的基础。同时,又是一个高效并发的经典设计案例。
本文链接:AQS的基本原理
作者:猴子007
出处:monkeysayhi.github.io
本文基于知识共享署名-相同方式共享 4.0国际许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名及链接。