一、底层原理:Monitor机制
-
对象锁与Monitor关联
synchronized
通过对象锁实现互斥,每个Java对象都可以关联一个Monitor(监视器),其底层由JVM用C++实现。当线程进入synchronized
代码块时,会尝试获取与锁对象关联的Monitor所有权。 -
Monitor内部结构
- Owner:记录当前持有锁的线程,同一时刻只能有一个线程成为Owner。
- EntryList :存储未抢到锁的线程,处于
Blocked
状态。 - WaitSet :存储调用
wait()
方法的线程,处于Waiting
状态。
-
锁竞争流程
- 线程首次尝试获取锁时,若Owner为空,则成为Owner并执行同步代码。
- 若Owner已被占用,线程进入EntryList阻塞。当Owner释放锁后,EntryList中的线程以非公平方式竞争锁。
- 若线程在同步代码中调用
wait()
,则会释放锁并进入WaitSet等待唤醒。
二、锁升级机制(JDK 1.6优化)
-
锁的三种形态
- 偏向锁:通过Mark Word中的线程ID标记锁的偏向状态,适用于无竞争场景。
- 轻量级锁:通过CAS操作替换Mark Word中的锁记录指针,适用于线程交替执行的低竞争场景。
- 重量级锁:传统Monitor实现,涉及用户态与内核态切换,性能较低。
-
升级条件
- 偏向锁升级:当其他线程尝试获取偏向锁时,JVM会撤销偏向锁并升级为轻量级锁。
- 轻量级锁升级:若CAS操作失败(竞争激烈),则膨胀为重量级锁。
三、与Lock的区别
-
语法层面
synchronized
是关键字,由JVM自动加锁/释放锁。Lock
是接口,需手动调用lock()
和unlock()
。
-
功能层面
- 共同点:均支持互斥、同步、锁重入(可重入性)。
- 差异 :
Lock
额外支持可中断、超时、公平锁、多条件变量等功能。
-
性能层面
- 无竞争时 :
synchronized
通过偏向锁和轻量级锁优化,性能更优。 - 高竞争时 :
Lock
(如ReentrantLock
)通常表现更好,因其减少了上下文切换。
- 无竞争时 :
四、使用方式
-
修饰实例方法
锁定当前对象实例(
this
),进入方法前需获取实例锁。 -
修饰静态方法
锁定类对象(
Class
对象),作用于所有实例。 -
修饰代码块
可指定任意对象(如
synchronized(obj)
)或类(synchronized(Class)
)作为锁。
五、其他特性
-
可重入性
线程可重复获取同一把锁,避免死锁。例如,递归调用同步方法不会阻塞。
-
锁释放
线程执行完同步代码或抛出异常时,JVM自动释放锁,无需手动干预。