什么是AQS
AbstractQueuedSynchronizer (抽象队列同步器,以下简称 AQS)出现在 JDK 1.5 中。AQS 是很多同步器的基础框架,比如ReentrantLock
、CountDownLatch
和 Semaphore
等都是基于 AQS 实现的。除此之外,我们还可以基于 AQS,定制出我们所需要的同步器。
在AQS内部,维护了一个FIFO队列(内部类Node节点)
和一个volatile的int类型的state
变量。在state=1的时候表示当前对象锁已经被占有了,state变量的值修改的动作通过CAS来完成。
为什么需要AQS
先说说锁和协作类有共同点:让线程“协作”的功能
CountDownLatch是一个计数器,它允许一个或多个线程等待其他线程完成操作。它通常用来实现一个线程等待其他多个线程完成操作之后再继续执行的操作。
CyclicBarrier是一个同步屏障,它允许多个线程相互等待,直到到达某个公共屏障点,才能继续执行。它通常用来实现多个线程在同一个屏障处等待,然后再一起继续执行的操作。
Semaphore是一个计数信号量,它允许多个线程同时访问共享资源,并通过计数器来控制访问数量。它通常用来实现一个线程需要等待获取一个许可证才能访问共享资源,或者需要释放一个许可证才能完成操作的操作。
ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
如果没有AQS
如果没有 AQS,那就需要每个线程协作工具类自己去实现至少以下内容
- 状态的原子性管理
- 线程的阻塞与解除阻塞
- 队列的管理
如果没有 AQS,就需要 ReentrantLock 等类来自己实现相关的逻辑,但是让每个线程协作工具类自己去正确并且高效地实现这些内容,是相当有难度的。AQS 可以帮我们把 “脏活累活” 都搞定,所以对于 ReentrantLock 和 Semaphore 等类而言,它们只需要关注自己特有的业务逻辑即可。正所谓是“哪有什么岁月静好,不过是有人替你负重前行”
AQS的同步队列和条件队列原理
同步的队列的实现原理
- 当一个线程尝试获取锁并失败时,AQS会将该线程包装成一个节点(Node)并加入到队列的尾部。
- 这个节点会处于等待状态,直到锁被其他线程释放。
- 当锁被释放时,头节点(持有锁的线程)会通知其后继节点(如果存在的话),后继节点尝试获取锁。
- 这个过程会一直持续,直到有线程成功获取锁或者队列为空。
条件队列的实现原理
从上面的结构图可以看到ConditionObject
是AQS的一个内部类主要用于实现条件变量。它允许一个或多个线程在特定条件成立之前等待,同时释放相关的锁。这在某种程度上类似于对象监视器模式中的wait()
和notify()
方法,但提供了更灵活和更强大的控制。
- 当线程调用了Condition的await()方法后,它会释放当前持有的锁,并且该线程会被加入到条件队列中等待。
- 然后线程会处于等待状态,直到其他线程调用
signal()
或signalAll()
方法来通知条件可能已经满足。