[Java-juc] 锁
2026年3月28日大约 5 分钟
[Java-juc] 锁
synchronized 用过吗?
用过,synchronized 是 Java 中最基本的锁机制。可以对代码块或方法上锁。
synchronized 上锁的对象是什么?
- 对于普通方法,锁对象是当前实例(this)。
- 对于静态方法,锁对象是当前类的 Class 对象。
- 对于代码块,可以自行指定任意对象作为锁对象。
synchronized 的底层实现原理是什么?
synchronized 依赖 JVM 内部的 Monitor 对象来实现线程同步。因此在使用的时候不用手动加锁解锁。
- synchronized 对 代码块 加锁时,JVM 会通过
monitorenter、monitorexit两个指令实现同步. - synchronized 对 方法 加锁时,JVM 会通过
ACC_SYNCHRONIZED标记符实现同步。
你对 Monitor 了解多少?
Monitor 是 JVM 内置的同步机制。
每个对象在内存中都有一个对象头——Mark Word,用于存储锁的状态,以及Monitor对象的指针。
synchronized 怎么保证可见性?
- 通过内存屏障
- 加锁时线程必须从主内存中读取最新数据
- 解锁时线程必须将修改后的数据刷新到主内存中
synchronized 怎么保证有序性?
- 通过 JVM 指令
monitorenter和monitorexit,确保代码块内的指令不会被重排。
synchronized 怎么实现可重入的呢?
synchronized支持可重入,是因为Java的对象头包含一个Mark Word,里面有一个锁计数器
- 当一个线程获取该对象锁时,JVM会将该线程ID写入Mark Word,并将锁计数器设置为1。
- 如果同一线程再次尝试获取该锁,JVM会检查Mark Word中的线程ID是否相同,相同则将锁计数器加1。
- 当线程释放锁时,JVM会将锁计数器减1,当计数器为0时,表示锁被完全释放,清除线程ID。
重点- synchronized 锁升级了解吗?
了解,锁升级是为提升Synchronized引入的,升级过程为
// 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁- 没有线程竞争时,使用偏向锁,没有额外CAS操作;
- 轻度竞争时,使用轻量级锁,采用CAS自旋,避免线程阻塞。
- 重度竞争时,升级为重量级锁,线程阻塞等待唤醒。
了解 synchronized 四种锁状态吗?
- 无锁:对象未被任何线程持有。
- 偏向锁:线程第一次获取锁,锁对象Mark Word记录线程ID,后续同一线程获取锁无需CAS操作。
- 轻量级锁:当有其他线程尝试获取偏向锁时,偏向锁升级为轻量级锁,使用CAS自旋等待锁释放。
- 重量级锁:当轻量级锁竞争激烈时,比如自旋超过一定次数,升级为重量级锁,线程进入阻塞状态等待唤醒。
重点- synchronized 和 ReentrantLock 的区别了解吗?
- synchronized 由JVM内部的Monitor机制实现;ReentrantLock在代码层面,基于AQS(AbstractQueuedSynchronizer)实现。
- synchronized 会隐式的自动加锁和释放锁;ReentrantLock 需要手动调用lock()和unlock()方法
- synchronized 可以在方法或代码块上加锁;ReentrantLock 只能在代码块中加锁,但可以指定锁的公平性。
Lock 了解吗?
Lock是JUC包下的接口,用于代码上实现锁机制,常用实现类有 ReentrantLock 和 ReentrantReadWriteLock。
重点- CAS 了解多少?
CAS 是一种乐观锁的一种实现。
- 首先 CAS 包含三个操作数:变量内存地址(V)、旧的预期值(A)和 新值(B)。
- 只有V 等于 A,才会将V 设置为 B。
- 这个过程需要使用
unsafe类中的方法去保证它的一个原子性。unsafe又是通过JNI去调用C++代码实现的。
重点- CAS 有什么问题?
- ABA问题:就是初始值A变成B在变回A,CAS无法检测到这个变化。- 可以通过版本号法解决。
- 自旋问题:当CAS失败时,线程会一直自旋尝试,会导致CPU资源浪费。- 可以限制自旋次数,超过次数转为synchronized。
- 只能保证单个变量的原子操作。- 可以将多个变量封装成一个对象,使用AtomicReference来解决。
AQS 了解多少?
AQS是Java并发包下的一个抽象同步框架,
核心作用是统一封装了线程的等待、唤醒、排队机制。
底层通过volatile的state变量,加FIFO的队列来实现线程安全的资源抢夺。
- AQS只是处理排队的细节,具体锁实现需要实现类自己决定。
重点- 说说 ReentrantLock 的实现原理?
ReentrantLock 是基于AQS实现的可重入排他锁,先使用CAS尝试获取锁,获取失败进入AQS的阻塞队列。
- ReentrantLock内部通过计数器 state 记录锁状态,重入+1,释放-1,state为0表示锁完全释放,唤醒等待队列的线程竞争锁。
ReentrantLock 怎么创建公平锁?
- ReentrantLock默认为非公平锁;创建公平锁,需要创建时传递参数
new ReentrantLock(true)。
非公平锁和公平锁有什么不同?
- 公平锁:按照线程请求锁的顺序来分配锁。
- 非公平锁:不保证锁的获取顺序,任何线程都有机会获取锁。
Java 有哪些保证原子性的方法?
Atomic包下的原子类;synchronized;ReentrantLock等。
原子操作类了解多少?
原子类是基于 CAS + volatile 实现的,底层依赖于 unsafe 类,常用原子类有:
- AtomicInteger、AtomicLong、AtomicReference 等。
线程死锁了解吗?
死锁发生在多个线程互相等待对方释放锁时。
死锁发生的四个条件了解吗?
- 互斥:资源不能被多个线程共享,一次只能被一个线程占用。
- 持有并等待:一个线程已持有资源,并且在等待获取其他线程持有的资源。
- 不可剥夺:资源不能强制被剥夺,必须由线程自己释放。
- 循环等待:存在一个线程等待资源的循环链,每个线程都在等待下一个线程持有的资源。
该如何避免死锁呢?
- 所有线程按照固定顺序获取锁。
- 使用 tryLock() 方法尝试获取锁,失败后释放已持有的锁,避免循环等待。
重点- 死锁问题怎么排查呢?
- 先使用
top命令找到对应程序的进程,再使用top -H -p 进程ID查看占用最高的线程ID,
最后使用jstack 进程ID查看线程堆栈信息,分析死锁原因。