1. AQS同步机制的本质解析
作为Java并发包的核心基石,AbstractQueuedSynchronizer(AQS)的同步设计堪称精妙。我在面试候选人时发现,90%的求职者能说出"独占和共享"这两个名词,但真正理解其实现差异的不足30%。让我们从JUC包设计者的视角,拆解这两种同步方式的底层逻辑。
AQS通过内置的FIFO队列管理竞争线程,其同步状态(state字段)的访问方式决定了两种模式:
- 独占模式(Exclusive):state被视作二进制信号量(0/1),同一时刻只允许一个线程持有锁。ReentrantLock就是典型实现,其tryAcquire()方法通过CAS操作将state从0改为1。
- 共享模式(Shared):state被当作计数器使用,允许多个线程同时获取资源。例如Semaphore的tryAcquireShared()会递减state值,只要结果非负就允许获取。
关键区别:独占模式的tryAcquire()返回boolean,而共享模式的tryAcquireShared()返回int(负值表示失败,非负表示成功且剩余资源数)
2. 独占式同步实战:ReentrantLock深度拆解
2.1 可重入锁的实现奥秘
以ReentrantLock的非公平锁实现为例,其同步过程分为三个关键阶段:
- 快速获取阶段:直接CAS修改state,不排队
java复制final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
- 队列管理阶段:通过addWaiter()将线程包装为Node加入CLH队列尾部
java复制private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 快速尝试入队
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); // 完整入队流程
return node;
}
- 阻塞唤醒阶段:通过LockSupport.park()/unpark()控制线程状态
2.2 公平与非公平的性能博弈
非公平锁的吞吐量比公平锁高30%-50%,这是因为它允许插队:
java复制// 非公平锁尝试获取逻辑
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) { // 不管有无排队线程直接抢
setExclusiveOwnerThread(current);
return true;
}
}
// ...重入逻辑
}
而公平锁会先检查队列:
java复制protected final boolean tryAcquire(int acquires) {
if (getFirstQueuedThread() != Thread.currentThread() &&
hasQueuedPredecessors()) // 关键判断
return false;
// ...后续逻辑相同
}
生产环境建议:除非必须保证绝对公平,否则优先选用非公平锁。数据库连接池等场景使用公平锁可能引发线程饥饿
3. 共享式同步精讲:Semaphore源码透视
3.1 信号量的运作机制
Semaphore的同步控制体现在state的原子操作上:
java复制final int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining; // 返回剩余许可数
}
}
释放许可时的传播机制:
java复制private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h); // 唤醒后继节点
}
// 传播唤醒信号
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head) break;
}
}
3.2 读写锁的混合模式
ReentrantReadWriteLock是两种模式的混合体:
- 写锁使用独占模式(state高16位记录)
- 读锁使用共享模式(state低16位记录)
其锁降级过程尤为精妙:
java复制// 获取写锁
writeLock.lock();
try {
// 写操作...
// 保持写锁的同时获取读锁(防止其他写线程干扰)
readLock.lock();
} finally {
writeLock.unlock(); // 降级为读锁
}
// 此时仍持有读锁
4. 高频面试难题破解
4.1 为什么AQS使用CLH队列?
CLH(Craig, Landin, Hagersten)队列的三个优势:
- 前驱节点的状态变更通过自旋检测,减少线程阻塞
- 队列只需维护tail指针,入队操作只需修改tail(对比MCS需要维护head和tail)
- 节点通过ThreadLocal保存前驱引用,减少内存争用
4.2 同步中断的底层处理
AQS对中断的响应分为两种策略:
- 不可中断模式(acquire())
- 仅记录中断状态(selfInterrupt())
- 不立即响应中断
- 可中断模式(acquireInterruptibly())
java复制private void doAcquireInterruptibly(int arg) { final Node node = addWaiter(Node.EXCLUSIVE); try { for (;;) { if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; return; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); // 直接抛异常 } } catch (...) { cancelAcquire(node); throw e; } }
4.3 条件变量的实现玄机
ConditionObject与同步队列的交互过程:
- await()时,线程节点从同步队列转移到条件队列
- signal()时,将节点从条件队列移回同步队列
- 转移过程中维持节点的waitStatus状态
java复制public final void await() throws InterruptedException {
Node node = addConditionWaiter(); // 加入条件队列
int savedState = fullyRelease(node); // 完全释放锁
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 重新竞争锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
}
5. 生产环境调优指南
5.1 锁竞争监控方案
通过ThreadMXBean检测死锁:
java复制ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads();
if (threadIds != null) {
ThreadInfo[] infos = bean.getThreadInfo(threadIds);
for (ThreadInfo info : infos) {
System.out.println(info.getLockName());
}
}
5.2 并发度优化策略
- 锁分解:将大锁拆分为多个小锁(如ConcurrentHashMap的分段锁)
- 锁粗化:合并连续的小锁请求(JIT编译器自动优化)
- 避免嵌套锁:预防死锁的黄金法则
5.3 AQS扩展实践
实现自定义同步器的模板:
java复制class CustomSync extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int arg) {
// 实现独占获取逻辑
}
protected boolean tryRelease(int arg) {
// 实现独占释放逻辑
}
protected int tryAcquireShared(int arg) {
// 实现共享获取逻辑
}
protected boolean tryReleaseShared(int arg) {
// 实现共享释放逻辑
}
}
在分布式限流场景中,可以基于AQS扩展实现:
java复制public class DistributedRateLimiter {
private final Sync sync;
private final Timer timer;
private static final class Sync extends AbstractQueuedSynchronizer {
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
void replenish(int permits) {
for (;;) {
int current = getState();
int next = current + permits;
if (compareAndSetState(current, next))
return;
}
}
}
}
