jUC中的锁

在JUC中 可以使用synchronized关键字进行加锁
如下所示

Object object = new Object();
synchronized (object){
//            TODO
}

synchronized关键字所加的锁是逐步升级的,顺序是

无锁-> 偏向锁 -> 轻量级锁 -> 重量级锁、

随着锁等级的提高,所带来的消耗也会越大。

在介绍锁直接之前,我们需要先引入一些概念。
在java中,对象由对象头+实例数据+填充数据(可选)组成。而对象头=Marklass+KClass+Body。其中Klass被用来存储对象指向类元数据的指针,虚拟机通过这个指针确定该对象是哪个类的实例。MarkWord则用来存储一些对象自身运行时数据,根据机器不同有32位和64位之分。以64位为例,它在不同锁状态下的结构如下所示
JUC-Monitor-MarkWord结构64位

下面将逐个介绍这些锁

偏向锁

首先是偏向锁,偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程之后重新获取该锁不再需要同步操作。

偏向锁是默认开启的,并且是有延迟的,不会在程序启动时默认生效。原因是在刚开始执行代码时,会有好多线程来抢锁,如果开偏向锁效率反而降低。

  • 使用jvm参数 -XX:BiasedLockingStartupDelay=0 可以禁用掉延迟
  • 使用jvm参数 -XX:-UseBiasedLocking 可以禁用掉偏向锁

当锁对象第一次被线程获得进入偏向状态时,会使用CAS操作将线程ID(53位)记录到Markword中 而且Markwod中的biased_lock=1 锁标志位会变成01。也就是MarkWord的后三位会由正常状态下的001变成101。如果 CAS 操作成功,这个线程以后进入这个锁相关的同步块,查看这个线程 ID 是自己的就表示没有竞争,就不需要再进行任何同步操作。当有其他线程来竞争锁对象时,偏向锁就会被撤销。

偏向锁的撤销

  • 当锁对象的hashcode被调用后,偏向锁机会被撤销,原因是在markword中记录线程ID的字段占用了hashcode的字段(在未偏向时使用hashcode会导致直接成为轻量级锁,在已偏向时调用hashcode会直接升级为重量级锁)。
  • 当有其他线程使用偏向锁对象时,会将偏向锁升级为轻量级锁
  • 调用 wait/notify时,notify会升级为轻量级锁,notify则会升级为重量级锁。
  • 批量撤销 如果对象被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID
  • 批量重偏向 当撤销偏向锁阈值超过 20 次后,JVM 会觉得是不是偏向错了,于是在给这些对象加锁时重新偏向至加锁线程
  • 批量撤销 当撤销偏向锁阈值超过 40 次后,JVM 会觉得自己确实偏向错了,根本就不该偏向,于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的

轻量级锁

当一个对象有多个对象要加锁,但是时间是错开的,JVM会使用轻量级锁来优化
轻量级锁在没有竞争时(锁重入时),每次重入仍然需要执行 CAS 操作,Java 6 才引入的偏向锁来优化。

加锁过程

  • 创建锁记录(Lock Record)对象,每个线程的栈帧都会包含一个锁记录的结构,存储锁定对象的 Mark Word

  • 让锁记录中 Object reference 指向锁住的对象,并尝试用 CAS 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录
    img

  • 如果 CAS 替换成功,对象头中存储了锁记录地址和状态 00(轻量级锁) ,表示由该线程给对象加锁
    img

  • 如果CAS 失败 则有两种情况

    • 一是锁重入 添加一条 Lock Record 作为重入的计数
      img
    • 二是其他线程已经持有该对象的轻量级锁 这时表明存在竞争 会将轻量级锁升级为重量级锁。

解锁过程

  • 如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减 1
  • 如果锁记录的值不为 null,这时使用 CAS 将 Mark Word 的值恢复给对象头
    • 成功,则解锁成功
    • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

重量级锁

当发生多个线程同时竞争时,轻量级锁就会升级为重量级锁,重量级锁的实现依赖于Monitor

Montior

Monitor 被翻译为监视器或管程。每个 Java 对象都可以关联一个 Monitor 对象,Monitor 也是 class,其实例存储在堆中,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针。
Montior由Owner EntryList WaitSet三部分组成
img
img

轻量级锁升级(锁膨胀)

在尝试加轻量级锁的过程中,CAS 操作无法成功,可能是其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁

  • 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁

  • Thread-1 加轻量级锁失败,进入锁膨胀流程:为 Object 对象申请 Monitor 锁,通过 Object 对象头获取到持锁线程,将 Monitor 的 Owner 置为 Thread-0,将 Object 的对象头指向重量级锁地址,然后自己进入 Monitor 的 EntryList BLOCKED

锁自旋

重量级锁竞争时,尝试获取锁的线程不会立即阻塞,可以使用自旋(默认 10 次)来进行优化,采用循环的方式去尝试获取锁

注意:

  • 自旋占用 CPU 时间,单核 CPU 自旋就是浪费时间,因为同一时刻只能运行一个线程,多核 CPU 自旋才能发挥优势
  • 自旋失败的线程会进入阻塞状态

优点:不会进入阻塞状态,减少线程上下文切换的消耗

缺点:当自旋的线程越来越多时,会不断的消耗 CPU 资源

自旋锁说明:

  • 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,比较智能
  • Java 7 之后不能控制是否开启自旋功能,由 JVM 控制

参考

本文内容参考以下内容
https://blog.csdn.net/Xin_101/article/details/117568632

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/781486.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Scanner的进阶使用——数字的输入

1.用Scanner输入数字(整数和小数) 1.定义一个整数变量2.建立扫描器3.使用if4.建立电脑接收数据5.设置else(那么)语法6.关闭Scanner

电磁学 数学储备

以新概念物理的附录为主要参考,总结了电磁学的部分数学基础。A 矢量的乘积和对称性 \[\def\ooint{{\bigcirc}\kern-11.5pt{\int}\kern-6.5pt{\int}} \def\oooint{{\bigcirc}\kern-12.3pt{\int}\kern-7pt{\int}\kern-7pt{\int}} \]矢量的标积 设\(\boldsymbol{A}\)和\(\boldsym…

面试题:64匹马,8个赛道,最少跑几次可以找出前四名?

面试题:64匹马,8个赛道,最少跑几次可以找出前四名? 一、常规非最优解法 均分比赛,高度为4的二叉树。次数为8+4+2+1=15 二、最优解综上,最少10次,最多11次。

华为pura70pro+ vs VIVO x100s pro

华为pura70pro+ vs VIVO x100s pro 简介 最近主力机不是很给力,老是发热卡顿,影响我正常的使用,于是有了换手机的想法。作为数码爱好者,有着使不完的折腾劲,我从三千块多的手机研究到了七八千的手机,从红米到小米,从oppo、vivo到华为等等等等研究了个遍。在五花八门的手…

多元/多维高斯/正态分布概率密度函数推导 (Derivation of the Multivariate/Multidimensional Normal/Gaussian Density)

各种维度正态分布公式: 一维正态分布二维正态分布/多维正态分布各向同性正态分布 注:即方差都是一样的,均值不一样,方差的值可以单独用标量表示。 多元/多维高斯/正态分布概率密度函数推导 (Derivation of the Multivariate/Multidimensional Normal/Gaussian Density) 作者…

痞子衡嵌入式:探析i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题(上篇)

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题探析。前段时间有一个 RT1052 客户反馈了一个有趣的问题,他们设计得是一个带 LCD 屏交互的应用,应用以官方 SDK 里的 lvgl_demo_widgets_bm 例程…

【书生浦语大模型实战营学习笔记】第二课 8G 显存玩转书生大模型 Demo

任务一:使用 Cli Demo 完成 InternLM2-Chat-1.8B 模型的部署,并生成 300 字小故事,记录复现过程并截图。配置好预置环境:300 字小故事:

Android网页投屏控制从入门到放弃

本文主要记录通过网页控制安卓设备相关的实践过程,通过从adb方案开始,到uiautomator2,以及最后放弃scrpy方案,在这个热闹的周末,正好闲暇的时间,了解过去不曾接触的知识,也是一个有趣的过程。背景 业务需要采集在app上执行任务的整个过程,原始方案相对复杂,修改需要协…

算法学习:矩阵快速幂/矩阵加速

1.前言其实本质上来说,矩阵快速幂或是矩阵加速的题目比较的模板化一些,大体上都是属于我们要先写出来一个递推式子(或者是我们需要递推的式子),然后由于递推的次数过大,1e18之类的,会导致复杂度的飚升,所以我们会用到矩阵来帮我们快速处理。另外,从题目的类型上大概是…

web渗透—RCE

一:代码执行相关函数1、eval()函数 assert()函数 (1)原理:将用户提交或者传递的字符串当作php代码执行 (2)passby:单引号绕过:闭合+注释;开启GPC的话就无法绕过(GPC就是将单引号转换为"反斜杠+单引号") eg: <?phphighlight_file(__FILE__);//高亮显示代码$…

代码块

代码块 概述:在Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。 (1)局部代码块 在方法中出现;限定变量生命周期,及早释放,提高内存利用率 (2)构造代码块 在类中方法外出现;多…