1. AQS 的核心原理?

AQS(AbstractQueuedSynchronizer):JUC 锁和同步器的"地基"。核心三要素:

加锁流程:tryAcquire 尝试 CAS 改 state → 成功则持有;失败则入队挂起(park)→ 前驱释放时 unpark 唤醒 → 重新竞争。

AQS 家族一览
ReentrantLock       // 可重入互斥锁(state = 重入次数)
Semaphore          // 信号量(state = 许可数)
CountDownLatch     // 倒计时门闩(state = 计数)
ReentrantReadWriteLock  // 读写锁(state 高16位读锁、低16位写锁)
ThreadPoolExecutor // Worker 也用 AQS 实现不可重入互斥
ArrayBlockingQueue // 条件队列 Conditon 基于 AQS

🎯 面试要点

  • 面试常问:手撕 AQS 简化版——答出"state + 双向队列 + CAS + park/unpark"就过关
  • CLH 队列:自旋(前驱节点轮询)在 Java 中改造为阻塞(park),叫"CLH 变体"
  • 为什么用前驱唤醒:每个节点只被自己的前驱唤醒,避免 notifyAll 式惊群

2. ReentrantLock 与 synchronized 的区别?公平锁和非公平锁?

区别:

公平/非公平实现:

ReentrantLock + Condition 实现精准唤醒
ReentrantLock lock = new ReentrantLock();
Condition notFull  = lock.newCondition();
Condition notEmpty = lock.newCondition();

// 生产者
lock.lock();
try {
    while (queue.isFull()) notFull.await();   // 等"不满"
    queue.add(x);
    notEmpty.signal();                           // 只唤醒消费者
} finally { lock.unlock(); }

// 消费者:notEmpty.await() / notFull.signal()——比 wait/notifyAll 精准,不会无效唤醒

🎯 面试要点

  • 一个 lock 可 new 多个 Condition(队列场景生产/消费分开唤醒)
  • 默认非公平:性能优先;业务上绝大多数场景非公平就够
  • LockSupport.park/unpark:与线程许可机制,unpark 可以先于 park(无超时风险)

3. CountDownLatch / CyclicBarrier / Semaphore 的区别?

工具 场景 复用性 关键方法
CountDownLatch 主线程等 N 个子任务完成("等门闩放下") 一次性,计数归 0 不可复用 countDown() / await()
CyclicBarrier N 个线程互相等齐后同时出发("凑人头") 可循环使用(reset) await() 凑齐 N 个放行
Semaphore 控制并发量(限流:最多 N 个同时访问) 可反复 acquire/release acquire() / release()
Semaphore 限流示例
// 最多 3 个线程同时访问数据库连接
Semaphore sem = new Semaphore(3);
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            sem.acquire();              // 拿许可,没有则阻塞
            // 访问受限资源...
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            sem.release();              // 归还许可
        }
    }).start();
}

🎯 面试要点

  • CountDownLatch:await 是主线程,countDown 是子线程(一等多)
  • CyclicBarrier:所有线程都 await,凑齐 N 个一起放行,可带 barrierAction(凑齐后执行的任务)
  • Semaphore 的 fair 参数同 ReentrantLock;释放必须是 finally

4. CAS 是什么?原子类怎么工作的?ABA 问题?

CAS(Compare And Swap):硬件级原子指令。`内存值 V、期望值 A、新值 B` —— 仅当 V == A 时把 V 更新为 B,否则失败重试。JUC 通过 Unsafe.compareAndSwapInt 调用,无锁实现原子更新。

原子类(java.util.concurrent.atomic):AtomicInteger 等,用 volatile 字段 + CAS。适合高并发计数器(比加锁吞吐高)。

ABA 问题:线程 1 读 A,线程 2 改 A→B→A,线程 1 CAS 时发现还是 A,误认为没被改过。解决:AtomicStampedReference(版本号)。实际业务中 ABA 大多无害(如栈场景可能有问题)。

CAS 自旋的朴素实现
public final int incrementAndGet() {
    int current;
    do {
        current = get();                       // 读当前值
    } while (!compareAndSet(current, current + 1));  // 失败就重试
    return current + 1;
}

// 自旋的代价:竞争激烈时空转浪费 CPU(可配合 Thread.yield 或退避)
// JDK8+ LongAdder 优化:分 Cell 计数,减少 CAS 竞争,适合高频统计

🎯 面试要点

  • CAS 缺点:自旋 CPU 开销、ABA、只能保证单个变量的原子性(多变量用锁)
  • LongAdder 热点打散思路在并发计数场景优于 AtomicLong
  • ConcurrentHashMap 的 put 空桶插入用的就是 CAS

5. JUC 并发容器有哪些?各解决什么问题?

🎯 面试要点

  • CopyOnWriteArrayList 迭代器是弱一致:迭代中修改不抛 ConcurrentModificationException
  • SynchronousQueue 常用于 Executors.newCachedThreadPool 的队列
  • 阻塞队列的核心:put 满则等、take 空则等(内部用 Condition)

🎤 常见面试追问

  1. AQS 的核心三要素?——volatile int state(同步状态)+ CLH 变体等待队列(抢锁失败入队,前驱释放唤醒后继)+ 模板方法(tryAcquire/tryRelease 由子类定义 state 语义)。
  2. 公平锁和非公平锁怎么实现的?——非公平:新线程直接 CAS 抢锁,抢不到再排队(吞吐高);公平:hasQueuedPredecessors() 检查队列有等待者就乖乖排队(无饥饿)。
  3. CountDownLatch 和 CyclicBarrier 的区别?——CountDownLatch:主线程等 N 个子任务完成(一等多),一次性;CyclicBarrier:N 个线程互相等齐再出发(多等多),可循环。
  4. CAS 的 ABA 问题是什么?怎么解决?——A→B→A:线程 1 读到 A,线程 2 改回 A,线程 1 CAS 误以为没变过。解决:AtomicStampedReference(版本号)。
  5. CopyOnWriteArrayList 读为什么不用加锁?——写时复制整个数组(新数组),读的是不变的旧数组快照,天然安全;适合读多写极少。

📖 名词解释(本页术语)

术语 大白话解释
AQSAbstractQueuedSynchronizer:JUC 锁和同步器的"地基"——用 state + 等待队列实现"获取/释放"框架,ReentrantLock/Semaphore 等都基于它。
ReentrantLock可重入锁(同一线程可重复获取):比 synchronized 多了可中断/可超时/可公平/多 Condition 能力。记得 finally 里 unlock。
CountDownLatch倒计时门闩:计数归零前 await 阻塞,countDown 减计数。用于"主线程等所有子任务完成"。
CyclicBarrier循环屏障:N 个线程都到达屏障才一起放行,可复用。用于"凑齐人再出发"(如并发测试同时开始)。
Semaphore信号量:控制同时访问的线程数(许可数)。acquire 拿许可、release 还许可——限流工具。
原子类(AtomicInteger 等)用 CAS 实现原子读改写的基本类型包装类,无锁高性能。LongAdder 是它的高并发优化版(分片计数)。
CopyOnWriteArrayList写时复制的线程安全 List:写操作复制数组,读不加锁。适合读多写极少(监听器列表)。
BlockingQueue阻塞队列:put 满则等、take 空则等。线程池的任务队列、生产者消费者用它。ArrayBlockingQueue/LinkedBlockingQueue 等。
Condition锁的"等待/通知"通道:一个 ReentrantLock 可以 new 多个 Condition,精准唤醒某一类等待者(比 wait/notifyAll 精准)。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。