传统同步机制

synchronized 和 Lock 是 Java 并发编程的核心工具,理解它们的原理和使用场景是面试必备。

synchronized 关键字 ⭐⭐⭐⭐⭐

三种使用方式

1. 修饰实例方法(锁当前对象)

public synchronized void method() {
    // 等价于 synchronized(this)
}

2. 修饰静态方法(锁 Class 对象)

public static synchronized void method() {
    // 等价于 synchronized(Demo.class)
}

3. 修饰代码块(锁指定对象)

public void method() {
    synchronized (lock) {
        // 临界区
    }
}

synchronized 原理 ⭐⭐⭐⭐⭐

底层实现

  • 修饰代码块:monitorentermonitorexit 指令

  • 修饰方法:ACC_SYNCHRONIZED 标志

对象头

Mark Word 锁状态

锁状态
存储内容
标志位

无锁

hashCode、GC 年龄

01

偏向锁

线程 ID、epoch

01

轻量级锁

指向栈中锁记录的指针

00

重量级锁

指向 Monitor 的指针

10

锁升级过程 ⭐⭐⭐⭐⭐

偏向锁 → 轻量级锁 → 重量级锁

示例


ReentrantLock ⭐⭐⭐⭐⭐

基本使用

ReentrantLock vs synchronized

特性
synchronized
ReentrantLock

锁类型

JVM 内置锁

API 层面锁

灵活性

低(自动释放)

高(手动释放)

公平锁

非公平锁

可选公平/非公平

可中断

❌ 不可中断

lockInterruptibly()

尝试获取锁

❌ 不支持

tryLock()

条件队列

1 个(wait/notify)

多个(Condition)

性能

JDK 6+ 优化后相当

相当

使用场景

简单同步

需要高级特性

ReentrantLock 高级特性

1. 公平锁 vs 非公平锁

2. 可中断锁

3. 尝试获取锁


ReadWriteLock ⭐⭐⭐⭐

读写锁原理

特点

  • 读锁(共享锁):多个线程可同时持有

  • 写锁(排他锁):只能一个线程持有,且阻塞读锁

基本使用

锁降级


线程协作工具 ⭐⭐⭐⭐⭐

wait/notify(Object 方法)

注意:必须在 synchronized 块中使用

生产者-消费者示例

Condition(Lock 的等待/通知)

优势:支持多个条件队列


并发工具类 ⭐⭐⭐⭐

CountDownLatch(倒计数门闩)

用途:等待多个线程完成

CyclicBarrier(循环屏障)

用途:多个线程相互等待,到达屏障后一起执行

CountDownLatch vs CyclicBarrier

  • CountDownLatch:一次性,计数不可重置

  • CyclicBarrier:可重复使用,适合循环任务

Semaphore(信号量)

用途:控制同时访问资源的线程数


面试要点 ⭐⭐⭐⭐⭐

Q1: synchronized 和 ReentrantLock 的区别?

  • synchronized 是 JVM 层面,ReentrantLock 是 API 层面

  • ReentrantLock 支持公平锁、可中断、tryLock

  • synchronized 简单,ReentrantLock 灵活

Q2: synchronized 的锁升级过程?

  • 偏向锁 → 轻量级锁 → 重量级锁

  • 减少锁竞争时的性能开销

Q3: wait() 和 sleep() 的区别?

  • wait() 释放锁,sleep() 不释放锁

  • wait() 是 Object 方法,sleep() 是 Thread 方法

  • wait() 需要在 synchronized 中使用

Q4: 什么时候使用 ReadWriteLock?

  • 读多写少的场景

  • 读操作并发,提升性能

Q5: CountDownLatch 和 CyclicBarrier 的区别?

  • CountDownLatch 一次性,CyclicBarrier 可重复使用

  • CountDownLatch 主线程等待工作线程,CyclicBarrier 工作线程相互等待

Q6: 公平锁和非公平锁的区别?

  • 公平锁按请求顺序获取,非公平锁允许插队

  • 非公平锁性能更好,但可能导致线程饥饿


参考资料

  1. 书籍推荐:《Java 并发编程实战》、《Java 并发编程的艺术》

  2. 源码:java.util.concurrent 包

Last updated