java并发相关(二)—— Synchronized的锁升级机制

一、Synchronized锁四个阶段概述

  java中synchronize锁分为以下四个阶段:

  • 无锁
  • 偏向锁
  • 轻量级锁
  • 重量级锁

  其中偏向锁和轻量级锁是从java1.6开始引入。各阶段之间的切换,如下图:

 

  从图中会发现,其实偏向锁是可以变成无锁的,这看似不符合我们认知中的锁可以升级不可以降级。单这种降级的本质,其实是偏向锁->偏向锁的一个过程。

二、Synchronized的锁升级机制

  2.1、无锁到偏向锁:

    我们知道,Synchronized修饰的方法被调用前,其对象初始状态是处于无锁状态的,其锁标记位为01,此时当线程a调用此方法时,会通过CAS自旋,替换mark words。

    偏向锁是默认开启的,而且开始时间一般是比应用程序启动慢几秒,如果不想有这个延迟,那么可以使用-XX:BiasedLockingStartUpDelay=0;

 

    如果不想要偏向锁,那么可以通过-XX:-UseBiasedLocking = false来设置;

   2.2、偏向锁到偏向锁:

    由于偏向锁线程1获取锁后,不会主动修改对象头,所以哪怕此线程1实际已消亡,之前加锁对象的对象头还是保持偏向锁状态。这个时候线程2想要进入同步方法,他会去查看线程1是否还存活,如果已经消亡,则把对锁定对象的对象头恢复成无锁,然后重复无锁->偏向锁的过程。

    同时如果线程1未消亡,但是其栈帧信息中不在需要此持有这个锁对象,也会进行一次偏向锁->偏向锁的过程。

  2.3、偏向锁到轻量级锁:

    2.2的情况中,如果线程2需要进入同步方法,线程1还持有这个对象,那么就会进入偏向锁->轻量级锁的过程。

    此时线程2进行cas替换失败,会修改对象头,升级为轻量级锁,同时开启自旋,重复尝试替换。

  2.4、轻量级锁到重量级锁:

    轻量级锁替换失败到达一定次数(默认为10)后,轻量级锁升级为重量级锁。

    需要注意,如果线程2自旋期间,有线程3也需要访问同步方法,则立刻由轻量级锁膨胀为重量级锁

    java1.6中,引入了自适应自旋锁,自适应意味着自旋 的次数不是固定不变的,而是根据前一次在同一个锁上自 旋的时间以及锁的拥有者的状态来决定。 如果在同一个锁对象上,自旋等待       刚刚成功获得过锁,并 且持有锁的线程正在运行中,那么虚拟机就会认为这次自 旋也是很有可能再次成功,进而它将允许自旋等待持续相 对更长的时间。

    关于重量级锁,我们之前说过其本质就是操作对象内部的监视器(monitor),这一点我们会在下一篇文章中进行详细分析。

posted @ 2019-10-16 23:37  豆豆323  阅读(2614)  评论(0编辑  收藏  举报