JAVA面试宝典-《并发编程必考:synchronized与AQS原理》

🔒《并发编程必考:synchronized 与 AQS 原理》

适合读者:
Java 多线程基础已扎实(Thread、Runnable),
熟悉 synchronizedReentrantLock 等同步机制,
想深度理解 synchronized 底层与 AQS 原理。


1️⃣ 引言:从线程安全谈起

想象一下:

  • 银行转账:两个线程同时修改同一账户余额,若不加锁,可能出现“丢钱”问题。
  • 火车站售票:多窗口售票,若没有同步机制,可能超卖或重复分配同一票。

线程安全 关键词:

  • 临界区(Critical Section)
  • 原子性(Atomicity)
  • 可见性(Visibility)
  • 有序性(Ordering)

要保证这些特性,Java 提供了多种同步机制,今天重点讲解:

  • JVM 内置的 synchronized
  • Java 层基于 AQS(AbstractQueuedSynchronizer)的锁

2️⃣ synchronized 的原理剖析

2.1 使用方式

public class TicketSeller {
    private int tickets = 100;

    // 1. 实例方法加锁
    public synchronized void sell() {
        if (tickets > 0) tickets--;
    }

    // 2. 静态方法加锁(锁 Class 对象)
    public static synchronized void staticSell() {}

    // 3. 代码块加锁
    public void sellBlock() {
        synchronized(this) {
            if (tickets > 0) tickets--;
        }
    }
}

日志示例:

Thread-1 获取锁
Thread-2 等待锁
Thread-1 卖出 1 张票
Thread-1 释放锁
Thread-2 获取锁

2.2 底层实现(JVM 层面)

synchronized 依赖 JVM 对象头的 Mark Word,分三个阶段:

  1. 偏向锁
  2. 轻量级锁(使用 CAS 自旋)
  3. 重量级锁(进入 OS 阻塞队列)
偏向锁 → 轻量级锁 → 重量级锁
   │           │             │
无竞争   少量竞争    高度竞争

图解锁升级流程

Thread A first acquires → Mark Word 标记为偏向 A  
Thread B blocks → 偏向锁撤销 → 两者都尝试 CAS(轻量级锁)  
多次 CAS 失败 → 重量级锁,OS 阻塞队列  

可用 jinfo -flag UseBiasedLocking、jstack 观察锁状态。

3️⃣ AQS:AbstractQueuedSynchronizer 原理详解

3.1 概念入门

AQS 是 JDK 提供的同步器框架,封装了独占锁(ReentrantLock)与共享锁(Semaphore、CountDownLatch)的队列管理逻辑。
底层使用 CLH 队列(虚拟节点链表),保证线程按 FIFO 排队。

3.2 核心机制解析

  • state:原子整型,表示锁状态
  • CLH 队列:Node 双向链表管理等待线程

加锁流程(独占模式)

acquire(arg):
  if tryAcquire(arg) succeed → 返回
  else
    node = addWaiter(EXCLUSIVE)
    parkAndCheckInterrupt(node)

  • tryAcquire:子类实现竞争逻辑(CAS 增加 state)
  • addWaiter:尾部插入等待队列
  • park:阻塞当前线程
    解锁流程:
release(arg):
  if tryRelease(arg) → unparkSuccessor(head)

  • tryRelease:CAS 减少 state
  • 唤醒队列头后继线程
    图示:线程排队与唤醒
[head][Thread A][Thread B][Thread C]
unlock() → 唤醒 A → A 成功加锁 → B、C 继续等待

3.3 自定义锁案例

public class SimpleLock extends AbstractQueuedSynchronizer {
    @Override
    protected boolean tryAcquire(int arg) {
        return compareAndSetState(0, 1);
    }
    @Override
    protected boolean tryRelease(int arg) {
        setState(0);
        return true;
    }
    public void lock() { acquire(1); }
    public void unlock() { release(1); }
}

4️⃣ synchronized vs AQS 对比分析

对比项synchronizedAQS(如 ReentrantLock)
实现方式JVM 内置、对象头 Mark WordJava 层 CAS + CLH 队列
是否可中断是 (lockInterruptibly)
公平性可选公平/非公平
尝试获取锁是 (tryLock)
性能JDK1.6+ 优化显著灵活,适合复杂场景

5️⃣ 常见面试高频问答

  1. synchronized 如何实现可重入?
    Mark Word 中记录拥有者线程和重入计数。
  2. AQS 为什么使用 CLH 队列?
    无需额外锁,链表节点自带排队信息,性能更好。
  3. AQS 为何用 CAS,而不直接 synchronized?
    避免内核阻塞,提升并发性能。
  4. ReentrantLock 怎么实现公平?
    构造时传入 true,tryAcquire 会检查队列头。

6️⃣ 总结与建议

  • 简单同步场景,首选 synchronized(语法简洁,JVM 已高度优化)。
  • 需可中断、公平锁、超时获取等功能时,使用 AQS(ReentrantLock、Semaphore)。
  • 深入理解 AQS,才能自己实现高性能并发组件。

🎓 掌握 synchronized 与 AQS,才能在高并发世界如鱼得水!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值