12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁

小陈:呼叫老王......

老王:来了来了,小陈你准备好了吗?今天我们来讲synchronized的锁重入、锁优化、和锁升级的原理

小陈:早就准备好了,我现在都等不及了

老王:那就好,那我们废话不多说,直接开始第一个话题:synchronized是怎么实现锁重入的?

synchronized的锁重入

小陈:老王,其实这个问题,之前我看了前几篇讲的,就知道了。

老王:哦,看来你是有准备的,那你来说说......

小陈:所谓锁重入,就是支持正在持有锁的线程支持再次获取锁,不会出现自己锁死自己的问题。

老王:嗯嗯,没错的

小陈:我打个比方,比如以下的代码:

synchronized(this) {

synchronized(this){

synchronized(this){

synchronized(this){

synchronized(this){

........

}

}

}

}

}

可能对应下面的指令:

monitorenter

monitorenter

monitorenter

monitorenter

monitorenter

......

monitorexit

monitorexit

monitorexit

monitorexit

monitorexit

回顾之前讲的加锁就是将_count 由 0 设置为1,将_owner指向自己,这里的_owner就是指向加锁的线程。

(1)所以再次重入加锁的时候,发现有人加锁了,同时检查_owner是不是自己加锁的,如果是自己加锁的,只需要将_count 次数加1即可。

(2)同样,在释放锁的时候执行monitorexit指令,首先将_count进行减1,当_count 减少到0的时候表示自己释放了锁,然后将_owner 指向null。

小陈:所以,根据上诉锁重入的方式,代码进入了5次synchronized 相当于执行了5次monitorenter加锁,最后_count = 5。 当5次monitorexit执行完了之后,_count = 0即释放了锁。

老王:很好,说得很详细,鼓掌鼓掌.......

锁消除

老王:小陈啊,锁重入这个你理解得很不错,锁消除这个你再来说说..

小陈:锁消除啊,这个也很简单,就是在不存在锁竞争的地方使用了synchronized,jvm会自动帮你优化掉,比如说下面的这段代码......

public void business() {

// lock对象方法内部创建,线程私有的,根本不会引起竞争

Object lock = new Object();

synchronized(lock) {

i++;

j++;

// 其它业务操作

}

}

上面的这段代码,由于lock对象是线程私有的,多个线程不会共享;像这种情况多线程之间没有竞争,就没必要使用锁了,就有可能被JVM优化成以下的代码:

public void business() {

i++;

j++;

// 其它业务操作

}

小陈:这就是我理解的锁消除,只有一个线程会用到,不会引起多个线程竞争的;相当于就自己用,没必要加锁了。

老王:嗯嗯,这个锁消除也理解的不错......

synchronized锁升级

老王:那synchronized的锁升级原理呢? 你来说说

小陈:额,这个锁升级其实我了解得不深,还比较模糊,还是老王您来讲吧......

老王:哈哈,好。讲解锁升级之前,我先问个问题,synchronized为什么要设计成可升级的锁呢?

小陈:这个,我理解的就是希望能尽量花费最小的代价能达到目的。

老王:嗯嗯,是这个理由没错;但是你知道synchronized在什么锁的情况下花费什么代价吗?以及每次升级之后花费了什么代价吗?

小陈:额,这个......,不清楚

老王:在说这个之前,我先给你看一下前两章都讲解过Mark Word的图,我们再来回顾一下:

之前我们说过,Mark Word是一个32bit位的数据结构,最后两位表示的是锁标志位,当Mark Word的锁标志位不同的时候,代表Mark Word 中记录的数据不一样。

(1)比如锁模式标志位是,也就是最后两位是01的时候

2025-05-18 14:43:35