四、锁升级

Java 1.6之前,synchronized是标准的重量级锁,多个线程竞争共享资源时,未竞争到资源的线程会一直处于阻塞状态,性能开销很大,同时对于重量级锁,对于加锁和释放锁也有很多的资源消耗。为了减少性能开销,提升效率,人们针对不同的加锁场景,细分了四种锁状态,包括无锁、偏向锁、轻量级锁,重量级锁,锁的状态会根据线程竞争资源的激烈程度从低到高不断升级。

4.1 偏向锁

很多时候,锁总是被同一线程多次获取,并没有线程竞争锁。对于这样的情况,偏向锁就很适用,那到底什么时候偏向锁呢?在第三章节,我们列出了在synchronized不同的锁状态下,Markword内存布局有很大的差异。

4.1.1 偏向锁获取

image.png

当一个线程去访问synchronized关键字修饰的代码块或方法时,会在Markword中存储当前线程的ID,当再有线程想尝试进入同步块时,会先通过CAS比较当前Markword存储的线程ID是否为尝试进入同步块的线程ID,如果相等,不需要再次获取锁了,可直接执行同步代码块;如果不相等,说明当前偏向锁是偏向于其它线程,需要撤销偏向锁,然后将锁升级成轻量级锁。

4.1.2 偏向锁撤销

撤销偏向锁并不是将锁真正的撤销,成为无锁的状态。对于偏向锁的撤销,对原持有的线程和锁本身有两种情况。

  • 如果原持有线程刚好执行完了,退出同步代码块,那么这个时候会把Markword保存的线程ID设置为空。
  • 如果原持有线程仍在同步代码块中执行,这个时候偏向锁会升级为轻量级锁,然后原有线程继续执行。

下面图演示在synchronized修饰的同步代码块下,线程T1和线程T2先后竞争锁资源的流程。

image.png

下一章谈谈轻量级和重量级锁。