文章目录
一、锁升级 (面试经常考)偏向锁 二、锁消除三、锁粗化锁的粒度 四、相关面试题
结合 锁策略,我们就可以总结出,synchronized
具有以下特性:
当代码执行到 synchronized
代码块中,JVM 大概要做哪些事情
一、锁升级 (面试经常考)
刚开始synchronized
加锁,首先锁会处于“偏向锁”状态遇到线程之间的锁竞争,升级到“轻量级锁”进一步的统计竞争出现的频次,达到一定程度之后,升级到“重量级锁”synchronized
加锁的时候,会经历:无锁 => 偏向锁 => 轻量级锁 => 重量级锁 轻量级锁和重量级锁在 各种锁策略这篇文章中有详细介绍
在这里我们理解一下什么是“偏向锁”
偏向锁
偏向锁不是真的加锁,因为真的加锁开销比较大,偏向锁只是做个标记,标记的过程,非常的轻量高效(比轻量级锁还要轻量)
偏向锁不是真的"加锁",只是给对象头中做⼀个"偏向锁的标记",记录这个锁属于哪个线程
如果后续没有其他线程来竞争该锁,那么就不⽤进⾏其他同步操作了(避免了加锁解锁的开销)如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了,很容易识别当前申请锁的线程是不是之前记录的线程),那就取消原来的偏向锁状态,就立即加锁,进⼊⼀般的轻量级锁状态偏向锁本质上相当于"延迟加锁",能不加锁就不加锁,尽量来避免不必要的加锁开销,但是该做的标记还是得做的,否则⽆法区分何时需要真正加锁
例子:
假设男主是⼀个锁,⼥主是⼀个线程。如果只有这⼀个线程来使⽤这个锁,那么男主⼥主即使不领证结婚(避免了⾼成本操作),也可以⼀直幸福的⽣活下去
但是⼥配出现了,也尝试竞争男主,此时不管领证结婚这个操作成本多⾼,⼥主也势必要把这个动作完成了,让⼥配死⼼
偏向锁 => 轻量级锁:出现竞争
轻量级锁 => 重量级锁:竞争激烈
上述锁升级的过程,主要也是为了能够让 synchronized
这个锁很好的适应不同的场景,就可以降低程序员的使用负担(程序员不用考虑太多,无脑使用 synchronized
就行了)
对于当前 JVM
的实现来说,上述锁升级的过程,属于“不可逆”(只能升级,不能降级),要想回到最初的状态,就需要再弄一个锁对象才可以
二、锁消除
编译器的优化策略
编译器会对你写的 synchronized
代码做出判定,判定这个地方是否确实需要加锁,如果这里没有必要加锁,就能够自动把 synchronized
给干掉(避免产生无意义的执行开销)
锁消除虽然存在,我们写代码的时候,也不能完全指望这个,无脑加锁
锁消除只是一个辅助效果,给我们兜底,就别指望全给我们兜这个底毕竟编译器的判定有时候也没那么准确,尤其是在多线程环境下的代码三、锁粗化
编译器的优化策略
锁的粒度
synchronized() {...}
里面的代码越多,“粒度越粗”代码越少,“粒度越细”得看代实际执行的代码,而不是有多少行,可能里面有方法调用,也算“代码量” 锁粗化就是把多个“细粒度”的锁,合并成“粗粒度”的锁
一次加解锁过程,就把四次操作全部涵盖进去了因为每次加锁都可能涉及到阻塞等待,如果拆成多次,那么总的阻塞时间就可能很长,为了缩短总的等待时间,提高执行效率,就可以使用“锁粗化”的操作
例子:汇报工作
领导给你汇报了三个活,让你今天搞定
领导下班的时候,你把这三个活都干完了,去向领导电话汇报