synchronized锁升级过程

78 阅读2分钟

JDK1.6之前synchronized使用的是重量级锁,JDK1.6之后进行了优化,拥有了无锁->偏向锁->轻量级锁->重量级锁的升级过程,而不是无论什么情况都使用重量级锁。

synchronized在修饰方法和代码块在字节码上实现方式有很大差异(代码块字节码是 monitorentermonitorexit ; 方法是acc_synchronized标志位, 相当于在方法前和方法后隐式的增加了monitorentermonitorexit指令),但是内部实现还是基于对象头的MarkWord来实现的。

1.偏向锁 当一段同步代码一直被同一个线程多次访问,由于只有一个线程那么该线程在后续访问时便会自动获得锁

2.轻量级锁 当有另外线程逐步来竞争锁的时候,就不能再使用偏向锁了,要升级为轻量级锁,竞争线程尝试CAS更新 对象头失败,会等待到全局安全点(此时不会执行任何代码)撤销偏向锁。 撤销需要等待全局安全点(该时间点上没有字节码正在执行),同时检查持有偏向锁的线程是否还在执行:

①. 第一个线程正在执行synchronized方法(处于同步块),它还没有执行完,其它线程来抢夺,该偏向锁会被取消掉并出现锁升级。 此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程会进入自旋等待获得该轻量级锁。

②. 第一个线程执行完成synchronized方法(退出同步块),则将对象头设置成无锁状态并撤销偏向锁,重新偏向 。

轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁

在OpenJDK8中,轻量级锁的自旋默认是开启的,最多自旋15次,每次自旋的时间逐渐延长。如果15次自旋后仍然没有获取到锁,就会升级为重量级锁。

自旋可以避免线程挂起和恢复所代码的性能消耗,而如果多个线程进行自旋操作,会导致性能急剧下降,要升级为重要级锁,让线程阻塞排队,sync是非公平锁,会导致锁饥饿!