[原文链接](AQS系列文章 | 并发编程基石:为什么需要AQS?) 这是AQS系列的第一篇,本文将通过同步问题的本质、传统同步方案、AQS的定位、AQS解决的典型问题四个方面让你建立对AQS的感性认知。
一、从生活中的“排队”说起
想象你在银行办业务,假如窗口只有一个柜员,但来了10个客户。这时候可能出现两种混乱:
-
所有人都挤到窗口前(竞态条件):谁先抢到位置谁办业务,其他人只能干等。
-
柜员被重复打扰(线程不安全):客户A正在办业务,客户B却强行插队提交材料,可能导致材料混乱。
这就是多线程并发问题的缩影:
当多个线程竞争同一资源时,如何保证有序、安全地访问?
二、同步问题的本质
1. 竞态条件(Race Condition)
// 典型例子:多线程转账
publicclassBankAccount {
privateintbalance=100; // 初始余额100元
// 线程不安全的转账方法
publicvoidtransfer(int amount) {
if (balance >= amount) {
balance -= amount; // 问题发生在这里!
}
}
}
假设两个线程同时操作一个账户进行转账:
-
线程A要转出80元,判断
balance >= 80
后即将扣款 -
此时线程B抢先转出50元,余额变为50元
-
线程A继续执行
balance -= 80
,导致余额变为-30元!
这就是典型的竞态条件:多个线程操作共享资源的顺序不可控。
2. 线程安全的核心诉求
要解决上述问题,必须实现:
-
原子性:转账操作(判断+扣款)必须一起打包执行不可分割
-
可见性:一个线程修改余额后,其他线程立刻看到最新值
-
有序性:代码执行顺序不被编译器或CPU随意优化
三、传统方案:synchronized的局限
Java早期使用synchronized
实现同步:
public synchronized void transfer(int amount) {
if (balance >= amount) {
balance -= amount;
}
}
这把“万能钥匙”解决了问题,但存在明显缺陷:Synchronized完全由JVM调度,它是非公平锁的典型代表,不支持公平锁;只能通过wait/notifyAll实现条件等待,无法实现多条件等待;获取锁时线程阻塞直到获取到锁,期间不支持超时取消;另外它只有独占模式,不支持共享。具体对比可查看下面表格。
局限性对比表
结论:synchronized像是“傻瓜相机”,而AQS则像“专业单反”——功能更强大,但需要更多学习成本。
四、AQS的定位:JUC核心发动机
1. 什么是AQS?
-
全称:AbstractQueuedSynchronizer(抽象队列同步器)
-
身份:
java.util.concurrent.locks
包中的抽象类 -
职责:为构建锁和同步器提供基础设施(如线程排队、状态管理)
2. 为什么说它是“基石”?
- • 数据统计:JUC包中超过80%的同步工具基于AQS实现。比如常见的
ReentrantLock
、Semaphore
、CountDownLatch
都是基于AQS 实现的。
- 设计哲学:AQS将通用流程(如线程排队)与定制逻辑(如是否允许重入)分离,不同的AQS子类只需要关注自身变化的部分,复杂的且通用流程李大师已经帮我们写好了。
五、AQS解决的典型问题
1. 公平锁 vs 非公平锁
-
非公平锁(默认)
允许插队,吞吐量高但可能“饿死”线程。非公平的意义在于后来者线程可以先尝试获取锁。即,线程B、C正在排队等待锁,持有锁的线程A释放锁时,刚好线程D进来,线程D不是乖乖去排队而是会先尝试获取锁,但这可能会造成排队中的线程长时间获取不到锁,这就是“线程饥饿”。 -
公平锁
严格按队列顺序,避免饥饿。获取不到锁就乖乖去排队,严格按先来后到的顺序。
2. 共享锁的威力
以Semaphore(信号量)为例:
Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问
semaphore.acquire(); // 获取许可证(state减1)
// 访问共享资源...
semaphore.release(); // 释放许可证(state加1)
-
资源控制:类似停车场剩余车位显示牌。共享锁在于它有一定量的资源,只要资源充足,后来者线程依然可以执行。
-
灵活性:可动态调整许可证数量。
六、总结:AQS的价值
AQS就像乐高积木:你不需要从零造轮子,只需组合它提供的基础模块(状态管理、线程排队),就能搭建出ReentrantLock、Semaphore等强大的同步工具。
下一篇预告:
《AQS核心原理:状态与队列机制》——我们将拆解AQS内部的神秘“计数器”和“排队区”,用动画演示线程如何有序等待!
如果对你有帮助,辛苦点赞、转发支持一下~关注【BiggerBoy】公众号,获取更多技术干货!