CPU|java面试——常见的锁和锁升级( 五 )


  • 如果为可偏向状态 , 则测试线程ID是否指向当前线程 , 如果是 , 进入步骤5 , 否则进入步骤3
  • 如果线程ID并未指向当前线程 , 则通过CAS操作竞争锁 。 如果竞争成功 , 则将Mark Word中线程ID设置为当前线程ID , 然后执行5;如果竞争失败 , 执行4
  • 如果CAS获取偏向锁失败 , 则表示有竞争 。 当到达全局安全点(safepoint)时获得偏向锁的线程被挂起 , 偏向锁升级为轻量级锁 , 然后被阻塞在安全点的线程继续往下执行同步代码 。 (撤销偏向锁的时候会导致stop the word)
  • 执行同步代码 。
  • 安全点:
    应用程序线程可以被安全地停止掉的那个时间点 , 就叫做安全点 。 这一术语也通常用来指代SWT暂停 。
    查看安全点日志:
    要查看安全点停顿 , 可以打开安全点日志 , 通过设置JVM参数 -XX:+PrintGCApplicationStoppedTime 会打出系统停止的时间 , 添加-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 这两个参数会打印出详细信息 , 可以查看到使用偏向锁导致的停顿 , 时间非常短暂 , 但是争用严重的情况下 , 停顿次数也会非常多;
    注意:安全点日志不能一直打开:
    如果在生产系统上要打开 , 再再增加下面四个参数: -XX:+UnlockDiagnosticVMOptions -XX: -DisplayVMOutput -XX:+LogVMOutput -XX:LogFile=/dev/shm/vm.log
    JVM开启和关闭安全点:
    • 开启偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
    • 关闭偏向锁:-XX:-UseBiasedLocking
    偏向锁的释放
    只有遇到锁竞争的时候 , 持有偏向锁的线程才会释放锁 , 否则线程不会主动去释放偏向锁 。 偏向锁的撤销时需要等待全局安全点(在这个时间点上没有字节码正在执行) , 它会首先暂停拥有偏向锁的线程 , 判断锁对象是否处于被锁定状态 , 撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态
    偏向锁如何升级为轻量级锁(00)?当偏向锁发生锁竞争的时候 , 偏向锁释放 , 然后相互竞争的线程在自己的线程栈内就会生成Lock Record 锁记录 , 然后每个线程之间通过自旋的方式竞争把这个锁记录写到mark Word里面 , 哪个线程最先修改成功, 谁就获取到这把锁 , 这就是轻量级锁 , 也叫自旋锁 。
    解释下为什么需要把轻量级锁升级到重量级锁?轻量级锁是处于用户态的 , 不需要向内核申请 , 所以比较快 , 但是重量级锁是放在内核态的 ,
    因为自旋锁是很消耗cpu的 , 如果获取到锁的那个线程一直在执行 , 不释放 , 这时候锁是需要升级的 ,
    升级成重量级锁的好处是什么?
    所以 , 关键就是重量级锁的队列里面 , 可以是wait状态 , 阻塞的 , 不消耗cpu资源 , 什么时候可以执行了 , 再唤醒
    轻量锁如何升级成为重量级锁(10)?竞争进一步加剧:自旋超过10次:-XXPreBlockSpin , 或者cpu核数的一半jdk1.6之后有了自旋自适应 , jdk自己会控制
    面试回答锁升级的过程?