Java锁synchronized关键字学习系列之轻量级锁

Posted c.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java锁synchronized关键字学习系列之轻量级锁相关的知识,希望对你有一定的参考价值。

Java锁synchronized关键字学习系列之轻量级锁

轻量级锁加锁原理

  1. 在代码访问同步资源时,如果锁对象处于无锁不可偏向状态,JVM首先将在当前线程的栈帧中创建一条锁记录(lock record),用于存放:
  • displaced mark word:存放锁对象当前的mark word的拷贝(为了解锁的时候恢复给锁对象,后面会说)
  • owner指针:指向当前的锁对象的指针,在拷贝mark word阶段暂时不会处理它

在这里插入图片描述

所以现在在栈帧中的lock record中的displaced mark word 存放的是锁对象处于无锁不可偏向状态的mark word的拷贝。

  1. 在拷贝mark word完成后,首先会挂起线程,jvm使用CAS操作尝试将对象的 mark word中的 lock record 指针指向栈帧中的锁记录。 这里我们来回顾一下对象头在不同锁状态下的结构。

在这里插入图片描述

前面说到当前的锁对象是无锁不可偏向状态的,在拷贝完mark word之后,就会尝试把除了标示位以外空间用来存放栈帧中lock record的地址。

并且会将锁记录中的owner指针指向锁对象的mark word。如下:

在这里插入图片描述

  • 如果CAS替换成功(说明当前锁对象的mark word跟栈帧中lock record中的displaced mark word是一样的),表示竞争锁对象成功,则将锁标志位设置成 00,并且现在 mark word中的 lock record 指针指向栈帧中的锁记录,表示对象处于轻量级锁状态,执行同步代码中的操作。

  • 如果CAS替换失败(说明当前锁对象的mark word跟栈帧中lock record中的displaced mark word是不一样的),则判断当前对象的mark word是否指向当前线程的栈帧

    • 如果是则表示当前线程已经持有对象的锁,执行的是synchronized的锁重入过程,可以直接执行同步代码块
    • 否则说明该其他线程已经持有了该对象的锁,如果在自旋一定次数后仍未获得锁,那么轻量级锁需要升级为重量级锁,将锁标志位变成10,后面等待的线程将会进入阻塞状态

在这里插入图片描述

在这里插入图片描述

上面这两张图都是参考别人的,参考链接在最下方。

轻量级锁释放锁原理

轻量级锁的释放同样使用了CAS操作,尝试将displaced mark word 替换回mark word(也就是把栈帧中lock record的displaced mark word 拷贝回锁对象的mark word),这时需要检查锁对象的mark word中lock record指针是否指向当前线程的锁记录:

  • 如果替换成功(锁对象的mark word中的lock record指针指向当前线程栈帧的lock record),则表示没有竞争发生,整个同步过程就完成了
  • 如果替换失败(锁对象的mark word中的lock record指针指向的不是当前线程栈帧的lock record),则表示当前锁资源存在竞争,有可能其他线程在这段时间里尝试过获取锁失败,导致自身被挂起,并修改了锁对象的mark word升级为重量级锁,最后在执行重量级锁的解锁流程后唤醒被挂起的线程

在这里插入图片描述

轻量级锁重入

synchronized是可以锁重入的,在轻量级锁的情况下重入也是依赖于栈上的lock record完成的。以下面的代码中3次锁重入为例:

synchronized (user){
    synchronized (user){
        synchronized (user){
            //TODO
        }
    }
}

轻量级锁的每次重入,都会在栈中生成一个lock record,但是保存的数据不同:

首次分配的lock record,displaced mark word复制了锁对象的mark word,owner指针指向锁对象。
之后重入时在栈中分配的lock record中的displaced mark word为null,只存储了指向对象的owner指针

在这里插入图片描述

轻量级锁中,重入的次数等于该锁对象在栈帧中lock record的数量,这个数量隐式地充当了锁重入机制的计数器。这里需要计数的原因是每次解锁都需要对应一次加锁,只有最后解锁次数等于加锁次数时,锁对象才会被真正释放。在释放锁的过程中,如果是重入则删除栈中的lock record,直到没有重入时则使用CAS替换锁对象的mark word。

总结

轻量级锁与偏向锁类似,都是jdk对于多线程的优化,不同的是轻量级锁是通过CAS来避免开销较大的互斥操作,而偏向锁是在无资源竞争的情况下完全消除同步。

最后放一个图来展示一下偏向锁和轻量级锁的升级

在这里插入图片描述

参考

synchronized和锁对象

再谈synchronized锁升级

synchronized轻量级锁的获取流程 | ProcessOn免费在线作图

轻量级锁的加锁过程 | ProcessOn免费在线作图,在线流程图

加锁 后何时解锁_轻量级锁加解锁过程详解

轻量级锁与偏向锁

带你彻底搞懂锁膨胀,偏向锁,轻量级锁,重量级锁

synchronized锁升级原理分析(偏向锁-轻量级锁-重量级锁)

以上是关于Java锁synchronized关键字学习系列之轻量级锁的主要内容,如果未能解决你的问题,请参考以下文章

Java锁synchronized关键字学习系列之重量级锁

Java锁synchronized关键字学习系列之轻量级锁

Java锁synchronized关键字学习系列之批量重偏向和批量撤销

Java锁synchronized关键字学习系列之轻量级锁升级

Java多线程系列---“基础篇”04之 synchronized关键字

Java并发编程系列之三JUC概述