话说江湖中,武林高手云集,各大门派都带着自己的独门秘籍来争夺武林至尊。而在 Java 的并发世界里,也有一个隐藏的“武林秘籍”,那就是AQS(AbstractQueuedSynchronizer)。
这套秘籍看似深奥,实则掌控着 Java 并发的半壁江山。无论是ReentrantLock(可重入锁)、Semaphore(信号量)、CountDownLatch(倒计时器),还是ReentrantReadWriteLock(读写锁),背后都离不开 AQS 的加持。
今天,小米就化身少林扫地僧,给大家掰开了、揉碎了,讲清楚 AQS 的“武学精髓”!
AQS 是什么?AQS,全称 AbstractQueuedSynchronizer,是 JDK 并发包(java.util.concurrent)中用于构建锁和同步器的基础框架。
用武侠比喻,AQS 就像是一个“武功心法”,它本身并不直接用于战斗(业务开发),但几乎所有高级武学(锁、同步器)都基于它来实现。
AQS 的核心思想:CLH 队列
AQS 底层采用了一种叫CLH(Craig, Landin, and Hagersten)队列的数据结构,它本质上是一个FIFO(先进先出)队列,所有的竞争线程都会排队等候锁的释放。
在 Java 中,AQS 维护了一个state 变量(代表锁的状态)和一个等待队列(双向链表),当多个线程争夺锁时,就会按照 FIFO 规则排队等待。
一个关键点:AQS 采用的是“模板方法模式”。也就是说,它定义了一整套获取、释放资源的流程,但具体的实现(如加锁、解锁逻辑)是交给子类去实现的。
所以,AQS 不能直接使用,而是被ReentrantLock、Semaphore、CountDownLatch 等类所继承,成为它们的核心实现机制。
AQS 的核心机制:独占锁 & 共享锁AQS 是一个支持独占模式(Exclusive)和共享模式(Shared)的同步器。
1. 独占模式:江湖中的“独门秘籍”
在独占模式下,某个线程获取了锁后,其他线程只能干等着。
举个栗子:ReentrantLock 的独占锁实现
tryAcquire(int arg):尝试获取锁,CAS 方式设置 state=1,成功返回 true,失败就进入等待队列。
tryRelease(int arg):释放锁,把 state 置为 0,并唤醒后续线程。
这个 MyLock 就是最基础的独占锁,它的实现机制和 ReentrantLock 如出一辙。
2. 共享模式:江湖中的“少林秘技”
在共享模式下,多个线程可以同时获取锁,但有一定的数量限制。
举个栗子:CountDownLatch 的共享锁实现
这个 MyLatch 实现了共享模式,允许多个线程等待某个条件完成再继续执行。
tryAcquireShared(int arg):当 state == 0 时,表示可以继续,否则线程进入等待队列。
tryReleaseShared(int arg):CAS 方式减少 state,当 state == 0 时,唤醒所有等待的线程。
这就是 CountDownLatch 的基本原理!
AQS 的核心方法在 AQS 里,所有锁的获取和释放,都是通过 acquire/release 方法来控制的。
AQS 通过这些方法,实现了公平锁、非公平锁、自旋锁、可重入锁等各种锁机制,真正做到了一套框架,适配万千武学。
AQS 的应用场景既然 AQS 这么强大,那它到底被用在哪些地方呢?
1. ReentrantLock(可重入锁)
独占模式
用于高并发场景下的线程安全控制。
2. Semaphore(信号量)
共享模式
控制同时访问资源的线程数量,适用于限流场景。
3. CountDownLatch(倒计时器)
共享模式
让多个线程等待某个条件达成。
4. ReentrantReadWriteLock(读写锁)
独占模式(写)+ 共享模式(读)
适用于读多写少的场景,提高性能。
总结:AQS 是 Java 并发的“武林秘籍”AQS 是 Java 并发包的核心,提供了独占模式和共享模式两种方式。
底层基于 CLH 队列,线程竞争锁时会排队等待,提高性能。
基于 AQS,JDK 提供了 ReentrantLock、Semaphore、CountDownLatch 等同步工具,极大提高了并发编程的效率。
END江湖路远,AQS 这本秘籍你学会了吗?如果你觉得这篇文章有帮助,记得点赞、关注、转发,小米继续给你带来更多 Java 技术分享!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!