Java 锁核心知识 + 高频面试题
一Java 锁核心基础知识
锁是多线程并发编程中,用于控制多个线程对共享资源互斥访问的机制,保证同一时刻只有一个线程操作数据,从而避免并发安全问题。



1. 锁的核心分类(按维度划分)
| 分类维度 | 具体类型 | 核心特点 |
|---|---|---|
| 锁的状态 | 偏向锁 / 轻量级锁 / 重量级锁(synchronized 升级路径) | 从无锁→偏向锁→轻量级锁→重量级锁,按需升级,减少锁竞争开销 |
| 锁的获取范围 | 悲观锁 / 乐观锁 | 悲观锁:认为一定会冲突(如 synchronized);乐观锁:认为大概率不冲突(如 CAS) |
| 锁的释放机制 | 可重入锁 / 不可重入锁 | 可重入:同一线程可重复获取同一把锁(如 ReentrantLock);不可重入:反之 |
| 锁的共享粒度 | 独占锁 / 共享锁 | 独占锁:同一时间仅一个线程获取(如 ReentrantLock);共享锁:多线程可共享(如 ReentrantReadWriteLock 读锁) |
| 锁的公平性 | 公平锁 / 非公平锁 | 公平锁:按排队顺序获取锁;非公平锁:可插队(默认,性能更高) |
Java 中实现锁的核心方式
Java 中实现锁主要分为两大方向:JVM 内置锁(无需手动实现,直接使用)和自定义锁(基于 AQS / 基础机制手动实现)
面试题 1:synchronized 的三种使用方式及底层原理
synchronized是 Java 内置的互斥锁(悲观锁),保证原子性、可见性和有序性,有三种使用形式:
1.修饰实例方法:锁对象是当前类的实例对象(this)
2.修饰静态方法:锁对象是当前类的Class 对象(全局锁)
3.修饰代码块:锁对象可自定义(实例对象 / Class 对象 / 任意对象)
底层原理:
-
JVM 通过监视器锁(Monitor) 实现,依赖操作系统的mutex lock(互斥锁)
-
实例方法 / 代码块:通过对象头的Mark Word存储锁状态
-
静态方法:锁存储在 Class 对象的 Mark Word 中
-
锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(优化性能)
public class SynchronizedDemo {
// 1. 修饰实例方法:锁是SynchronizedDemo的实例对象
public synchronized void instanceMethod() {
System.out.println("实例方法锁:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟业务逻辑
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 2. 修饰静态方法:锁是SynchronizedDemo.class(全局锁)
public static synchronized void staticMethod() {
System.out.println("静态方法锁:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 3. 修饰代码块:自定义锁对象
private final Object lock = new Object();
public void codeBlockMethod() {
synchronized (lock) { // 锁对象:lock实例
System.out.println("代码块锁:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 测试
public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
// 实例方法锁:demo1和demo2是不同对象,不互斥
new Thread(demo1::instanceMethod, "线程1").start();
new Thread(demo2::instanceMethod, "线程2").start();
// 静态方法锁:全局唯一,互斥
new Thread(SynchronizedDemo::staticMethod, "线程3").start();
new Thread(SynchronizedDemo::staticMethod, "线程4").start();
// 代码块锁:lock是demo1的成员,demo1的调用互斥,demo2不影响
new Thread(demo1::codeBlockMethod, "线程5").start();
new Thread(demo2::codeBlockMethod, "线程6").start();
}
}
面试题 2:synchronized 和 Lock 的区别
| 维度 | synchronized | Lock(如 ReentrantLock) |
|---|---|---|
| 锁获取方式 | 自动获取 / 释放(出作用域自动释放) | 手动获取(lock ())/ 释放(unlock ()) |
| 锁类型 | 非公平锁(默认) | 支持公平锁 / 非公平锁(构造方法指定) |
| 中断响应 | 不支持中断 | 支持中断(lockInterruptibly ()) |
| 超时获取 | 不支持 | 支持(tryLock (long time, TimeUnit)) |
| 条件变量 | 单一条件队列 | 支持多个 Condition(newCondition ()) |
| 底层实现 | JVM 层面(监视器锁) | 底层基于 AQS(AbstractQueuedSynchronizer) |
使用场景:
-
synchronized:适合简单场景(如单方法同步),代码简洁,无需手动释放
-
Lock:适合复杂场景(如超时获取、公平锁、多条件唤醒),灵活性更高
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class LockVsSynchronizedDemo {
// Lock示例:公平锁 + 多条件唤醒
private static final ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
private static final Condition condition1 = fairLock.newCondition();
private static final Condition condition2 = fairLock.newCondition();
public static void lockDemo() throws InterruptedException {
fairLock.lock(); // 手动加锁
try {
System.out.println(Thread.currentThread().getName() + "获取锁");
condition1.await(); // 等待条件1
System.out.println(Thread.currentThread().getName() + "被唤醒");
} finally {
fairLock.unlock(); // 必须在finally中释放,避免死锁
}
}
// 测试Lock的超时获取
public static void tryLockDemo() {
try {
// 尝试1秒内获取锁,获取失败则返回false
if (fairLock.tryLock(1, java.util.concurrent.TimeUnit.SECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + "超时获取锁成功");
} finally {
fairLock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + "超时获取锁失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
// 测试超时获取锁
new Thread(LockVsSynchronizedDemo::tryLockDemo, "线程A").start();
fairLock.lock(); // 主线程先获取锁
Thread.sleep(2000); // 持有2秒,让线程A超时
fairLock.unlock();
// 测试多条件唤醒
new Thread(LockVsSynchronizedDemo::lockDemo, "线程B").start();
Thread.sleep(1000);
fairLock.lock();
condition1.signal(); // 唤醒等待condition1的线程B
fairLock.unlock();
}
}
面试题 3:什么是死锁?如何避免和排查?
死锁定义: 多个线程互相持有对方需要的锁,且都不释放,导致所有线程永久阻塞。
死锁产生的 4 个必要条件
-
互斥:锁只能被一个线程持有
-
占有且等待:线程持有一个锁,同时等待另一个锁
-
不可抢占:线程持有的锁不能被其他线程强制抢占
-
循环等待:线程 A 等待线程 B 的锁,线程 B 等待线程 A 的锁
public class DeadLockDemo {
// 定义两个锁对象
private static final Object lockA = new Object();
private static final Object lockB = new Object();
// 线程1:先拿lockA,再拿lockB
private static void thread1() {
new Thread(() -> {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "持有lockA,等待lockB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获取lockB,执行完成");
}
}
}, "线程1").start();
}
// 线程2:先拿lockB,再拿lockA(导致循环等待)
private static void thread2() {
new Thread(() -> {
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "持有lockB,等待lockA");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "获取lockA,执行完成");
}
}
}, "线程2").start();
}
// 修复死锁:统一锁的获取顺序(破坏循环等待)
private static void fixedThread1() {
new Thread(() -> {
synchronized (lockA) { // 先拿lockA
System.out.println(Thread.currentThread().getName() + "持有lockA,等待lockB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获取lockB,执行完成");
}
}
}, "修复线程1").start();
}
private static void fixedThread2() {
new Thread(() -> {
synchronized (lockA) { // 统一先拿lockA,再拿lockB
System.out.println(Thread.currentThread().getName() + "持有lockA,等待lockB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获取lockB,执行完成");
}
}
}, "修复线程2").start();
}
public static void main(String[] args) {
// 产生死锁
// thread1();
// thread2();
// 修复死锁
fixedThread1();
fixedThread2();
}
}
面试题 4:volatile 和锁的区别

public class VolatileVsLockDemo {
// volatile变量:保证可见性,但不保证原子性
private static volatile int count = 0;
// 锁对象:保证原子性
private static final Object lock = new Object();
// 测试volatile的原子性问题
private static void volatileTest() {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
count++; // 非原子操作:读取→自增→写入
}).start();
}
}
// 测试锁保证原子性
private static void lockTest() {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
synchronized (lock) {
count++; // 原子操作
}
}).start();
}
}
public static void main(String[] args) throws InterruptedException {
// volatileTest();
lockTest();
Thread.sleep(2000);
System.out.println("最终count值:" + count);
// volatileTest执行结果:小于1000(原子性问题)
// lockTest执行结果:等于1000(原子性保证)
}
}

浙公网安备 33010602011771号