AQS 全称 AbstractQueuedSynchronizer,是 java.util.concurrent.locks 包下面的一个类。用来构建锁和同步器的框架,使用 AQS 能简单而高效地构造出应用广泛的大量同步器,如ReentrantLock,Semaphore,ReentrantrantReadWriteLock,SynchronousQueue,FutureTask 等。开发者也可以利用 AQS 构造出符合要求的同步器。
AQS 的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态。如果被请求的共享资源被占用,就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin, and Hagersten)队列是一个虚拟的是双向队列(虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系)。AQS 是将每条共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。
AQS 使用一个 int 成员变量来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。使用 CAS 对该同步状态进行原子操作实现对其值的修改。
共享方式
- Exclusive(独占)只有一个线程能执行,如 ReentrantLock。独占锁又分为公平锁和非公平锁。公平锁按照线程在队列中的排队顺序,先到者先获取锁;非公平锁在线程需要获取锁时,无视队列顺序直接去抢锁,谁抢到算谁的。
- Share(共享):多个线程可同时执行,如 Semaphore、CountDownLatch、CyclicBarrier、ReadWriteLock
实现原理
同步器设计基于模板模式,自定义同步器一般的方式为:
- 继承 AbstractQueuedSynchronizer 并重写指定方法,这些方法是对共享资源 state 的获取和释放
- 将 AQS 组合在自定义同步组件的实现中,并调用其它模板方法,这些模板方法会调用使用者重写的方法
AQS 使用模板方法模式,自定义同步器时重写以下几个 AQS 提供的模板方法:
1 | isHeldExclusively(); // 线程是否在独占资源,用到 condition 时才需要实现 |
默认情况下,每个方法都抛出UnsupportedOperationException
,这些方法的实现必须是线程安全,并且应该简短而不阻塞。AQS 中的其它方法都是 final的,无法被其它类使用。
CyclicBarrier 和 CountDownLatch 的区别
CountDownlatch 是计数器,只能使用一次,计数为 0 时释放所有等待线程;CyclicBarrier 的计数器提供 reset 功能,可以使用多次。