java锁是如何实现的
Java锁的实现机制
Java中的锁机制主要通过synchronized关键字和java.util.concurrent.locks包下的显式锁(如ReentrantLock)实现。以下是两种锁的核心实现原理:
synchronized的实现
synchronized基于JVM内置的监视器锁(Monitor)实现,依赖对象头中的Mark Word和线程栈帧中的锁记录(Lock Record)。
对象头结构
32位JVM的Mark Word布局(以偏向锁为例):
|-------------------------------------------------------|--------------------|
| Mark Word (32 bits) | State |
|-------------------------------------------------------|--------------------|
| hashcode:25 | age:4 | biased_lock:1 | lock:2 (01) | Normal |
| thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 (01)| Biased |
| ptr_to_lock_record:30 | lock:2 (00)| Lightweight Locked |
| ptr_to_heavyweight_monitor:30 | lock:2 (10)| Heavyweight Locked |
| | lock:2 (11)| Marked for GC |
|-------------------------------------------------------|--------------------|
锁升级过程
- 无锁状态:初始状态,对象未被任何线程锁定。
- 偏向锁:通过CAS设置Mark Word中的线程ID,适用于单线程重复访问场景。
- 轻量级锁:当多线程竞争时,撤销偏向锁,线程通过CAS将Mark Word替换为指向栈中锁记录的指针。
- 重量级锁:竞争加剧时,轻量级锁升级为重量级锁,未获取锁的线程进入阻塞状态,依赖操作系统的互斥量(Mutex)。
ReentrantLock的实现
ReentrantLock基于AQS(AbstractQueuedSynchronizer)实现,核心通过CAS操作和CLH队列管理线程阻塞与唤醒。
AQS核心结构
- state字段:表示锁的重入次数(如0表示未锁定,≥1表示被持有)。
- CLH队列:双向链表结构的等待队列,节点保存阻塞线程信息。
加锁流程(非公平锁为例)
final void lock() {
if (compareAndSetState(0, 1)) // CAS尝试直接获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 进入AQS排队逻辑
}
AQS的acquire方法
- 尝试通过
tryAcquire获取锁(子类实现,如检查state是否为0)。 - 失败后将线程封装为Node加入队列尾部,通过
LockSupport.park阻塞线程。 - 前驱节点释放锁时,唤醒后续节点重新竞争。
锁的对比
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现层级 | JVM内置 | JDK代码层面 |
| 公平性 | 非公平 | 支持公平/非公平(构造函数指定) |
| 中断响应 | 不支持 | 支持lockInterruptibly |
| 条件变量 | 单一wait/notify |
支持多个Condition |
| 锁释放 | 自动(代码块结束) | 需手动unlock |
性能优化建议
- 偏向锁:适用于单线程占优场景,可通过JVM参数
-XX:+UseBiasedLocking启用(JDK 15后默认禁用)。 - 自旋优化:重量级锁竞争时,线程会自旋尝试获取锁(通过
-XX:PreBlockSpin调整自旋次数)。 - 锁消除:JIT编译器对不可能存在竞争的锁进行消除(如局部对象锁)。
- 锁粗化:将相邻的同步块合并减少锁开销。
通过合理选择锁类型和优化参数,可以平衡线程安全与性能需求。







