Java 中关于锁的面试题经常涉及 锁的类型、锁的机制、锁的优化以及锁的底层原理。以下是一些高频面试题,并附带详细解答,希望能帮助你准备面试!
Class
对象,影响同一个类的所有实例)synchronized
和 ReentrantLock
。ReentrantLock(true)
。ReentrantLock(false)
(默认非公平锁)。ReentrantLock
。ReadWriteLock
。synchronized
和 ReentrantLock
的区别?对比项 | synchronized |
ReentrantLock |
---|---|---|
锁的类型 | 由 JVM 实现 | 由 JDK 提供 |
可重入性 | 是(一个线程可以多次获取同一锁) | 是 |
公平性 | 非公平锁 | 支持公平和非公平(默认非公平) |
读写分离 | 不支持 | ReadWriteLock 支持读写分离 |
阻塞与非阻塞 | 阻塞式 | 可选非阻塞 tryLock() |
可中断性 | 不支持 | lockInterruptibly() 支持可中断 |
超时机制 | 不支持 | tryLock(timeout, TimeUnit.SECONDS) |
总结:
synchronized
适合简单的同步场景,性能更好(JVM 进行了大量优化)。ReentrantLock
适合复杂的并发控制,如公平锁、可中断锁、超时锁等。synchronized
底层是如何实现的?synchronized
依赖于 JVM 对象头的 Mark Word,通过 锁升级机制(偏向锁 → 轻量级锁 → 重量级锁) 实现高效同步。其底层实现涉及:
synchronized
主要依赖 JVM 内部的 monitorenter
和 monitorexit
指令,它会使用 对象头的 Mark Word
记录锁状态。
public class SyncDemo {
private final Object lock = new Object();
public void method() {
synchronized (lock) {
System.out.println("线程安全的方法");
}
}
}
javap -v SyncDemo.class
会看到:
monitorenter // 进入 synchronized 块
monitorexit // 退出 synchronized 块
CAS 是无锁并发的一种实现方式,用于原子操作,如 AtomicInteger
。它的核心是 比较并交换:
expectedValue
)。示例:
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
count.compareAndSet(0, 10); // 只有当 count 为 0 时,才更新为 10
System.out.println(count.get()); // 输出 10
}
}
✅ 优点:
synchronized
竞争。❌ 缺点:
AtomicStampedReference
加时间戳)。AtomicReference
或 synchronized
)。死锁发生时,多个线程相互等待,导致程序无法继续执行。死锁的四个必要条件:
class DeadLockDemo {
private static final Object LOCK1 = new Object();
private static final Object LOCK2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (LOCK1) {
System.out.println("线程1 持有 LOCK1");
synchronized (LOCK2) {
System.out.println("线程1 获取 LOCK2");
}
}
}).start();
new Thread(() -> {
synchronized (LOCK2) {
System.out.println("线程2 持有 LOCK2");
synchronized (LOCK1) {
System.out.println("线程2 获取 LOCK1");
}
}
}).start();
}
}
tryLock()
(避免无限等待)。Thread.dumpStack()
检测死锁。自旋锁 是指线程在获取锁时不会立即阻塞,而是 不断尝试获取锁(自旋),直到成功。
JVM 默认自旋 10 次(可通过 -XX:PreBlockSpin
调整)。
优点:减少线程切换,提高性能。
缺点:若竞争激烈,会浪费 CPU 资源。
这些是 Java 并发锁的常见面试题,涉及锁的类型、底层原理、CAS、死锁以及锁优化,熟练掌握后能应对大多数面试!