小陈:呼叫老王......
老王:来了来了,小陈你准备好了吗?今天我们来讲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的时候