Java如何避免重量级锁,Java 中锁是如何一步步膨胀的(偏向锁、轻量级锁、重量级锁)...

文章目录

重量级锁(Mutex Lock)

偏向锁(比较 ThreadID)

偏向锁获取过程

偏向锁的释放

轻量级锁(自旋)

轻量级锁的加锁过程

轻量级锁的释放

总结

重量级锁(Mutex Lock)

Synchronized 是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的 Mutex Lock 来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么 Synchronized 效率低的原因。因此,这种依赖于操作系统 Mutex Lock 所实现的锁我们称之为“重量级锁”。JDK 中对 Synchronized 做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6 以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了自旋锁、自适应自旋锁、轻量级锁和偏向锁。

明确 Java 线程切换的代价,是理解java中各种锁的优缺点的基础之一。

Java 对象头中 markword 结构。Java 对象的内存布局及访问方式

cc158ecc1cbff7c99eeb4cf73b7a4792.png

偏向锁(比较 ThreadID)

Hotspot 的作者经过以往的研究发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得。偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起来让这个线程得到了偏护。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的 CAS 原子指令的性能消耗)。轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。

偏向锁获取过程

访问 Mark Word 中锁标志位是否为 01,是的话查看偏向锁的标识,如果是 1,则确认为可偏向状态;如果是 0 则为无锁状态,直接通过 CAS 操作竞争锁,如果竞争失败,执行4。

如果为可偏向状态,则测试线程 ID 是否指向当前线程,如果是,进入步骤5,否则进入步骤3。

如果线程 ID 并未指向当前线程,则通过 CAS 操作竞争锁。如果竞争成功,则将 Mark Word 中线程 ID 设置为当前线程 ID,然后执行5;如果竞争失败,执行4。

如果 CAS 获取偏向锁失败,则表示有竞争,开始锁撤销。

执行同步代码。

偏向锁的释放

偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁。

当获得偏向锁的线程到达全局安全点(safepoint)时暂停该线程,检查该线程的状态。

如果该线程存活且没有退出同步代码块,则升级为轻量级锁,并唤醒该线程从安全点继续执行。

如果该线程没有存活或者该线程已退出同步代码块,则将偏向锁撤销为无锁状态(锁标志位 01,偏向锁标识 0)唤醒该线程。

到达安全点 safepoint 会导致 stop the word,时间很短。

eb27535dea8b900495d6cfc6e2780284.png

轻量级锁(自旋)

轻量级是相对于使用操作系统互斥量来实现的传统锁而言的。但是,首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。在解释轻量级锁的执行过程之前,先明白一点,轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。

轻量级锁的加锁过程

1、访问对象的 Mark Word 中锁标识位,如果为 00,JVM 会先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝,官方称之为 Displaced Mark Word。这时候线程堆栈与对象头的状态如图所示。(注:该图仅作为参考,我认为该图可能存在问题)

2b301187e659c0c840391c5198ed2ee4.png

2、将对象头中的 Mark Word 复制到锁记录中;

3、拷贝成功后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针,并将 Lock Record 里的 owner 指针指向 object mark word。如果更新成功,则执行步骤4,否则执行步骤5。

4、如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,此时 Mark Word 的锁标识为 00,这时候线程堆栈与对象头的状态如图所示。(注:该图仅作为参考,我认为该图可能存在问题)

6a3243a8acbcf5511d19b43dbb2db676.png

5、如果这个更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则当前线程便尝试使用自旋来获取锁(自旋就是为了不让线程阻塞,而采用循环去获取锁的过程),自旋达到一定次数后 CAS 操作依然没有成功,轻量级锁就要膨胀为重量级锁,锁标识设置为 10,Mark Word 中存储的就是指向重量级锁(monitor)的指针,当前线程以及后面等待锁的线程便会进入阻塞状态。

轻量级锁的释放

轻量级锁解锁时,持有锁的线程会使用 CAS 原子操作将 Mark Word 替换回到对象头,如果成功,则表示没有竞争发生。如果失败,释放锁并唤醒那些被挂起的线程。

总结

偏向锁

偏向锁只会在第一次请求锁时使用 CAS 操作,并在锁对象的标记字段中记录当前线程 ID。在此后的运行过程中,仅需比较线程 ID,消除这个线程锁重入(CAS)的开销。针对的是锁仅会被同一线程持有的状况。

轻量级锁

轻量级锁采用 CAS 操作,减少了传统的重量级锁使用产生的性能消耗。针对的是多个线程在不同时间段申请同一把锁的情况。

重量级锁

重量级锁会阻塞、唤醒请求加锁的线程,会导致线程上下文切换。针对的是多个线程同时竞争同一把锁的情况。JVM 采用自适应自旋,来避免在面对非常小的同步代码块时,仍会被阻塞和唤醒的状况。

参考文章:

https://blog.csdn.net/zhao_miao/article/details/84500771

https://blog.csdn.net/zqz_zqz/article/details/70233767

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

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

相关文章

Flexbox布局

Flexbox布局 刚开始接触flex布局的时候,只知道它可以用来使子元素水平垂直居中,代码最为简洁好用。 .container {display: flex;justify-content: center;align-items: center; } 当然不仅仅是居中问题,flexbox能做的事情大多&#xf…

CSS_常见布局

1.一列布局——常用于网站首页。 html&#xff1a; 1 <div class"top"></div> 2 <div class"main"></div> 3 <div classfoot></div> css&#xff1a; 1 body{2 margin:0;3 padding: 0;4 …

会员系统用php框架,代码基地会员中心的PHP签到系统结合thinkphp框架

分享代码基地会员中心的PHP签到系统&#xff0c;PHP很漂亮的签到界面。签到后&#xff0c;会有不同颜色区分。附带PHP签到源码带安装说明和详细注释。(详细查看https://www.daimajidi.com/deal/574)JS:function getUrl(strs) {var url "/demo/1563064903/" strs;re…

Spring MVC:表单处理卷。 1个

Spring MVC是Spring Framework的一部分&#xff0c;其主要目的是使Web开发更加简单&#xff0c;便捷和轻松。 与表单的交互是或多或少现代Web应用程序的一部分。 Spring MVC允许您以非常严格和简单的方式执行各种形式的活动。 在本文中&#xff0c;您将在Spring MVC的帮助下阅读…

Java监视器绑定的超人

这是超人生活中的黑暗时期。 乔尔艾尔&#xff08;Jor-El&#xff09;希望他继续航行&#xff0c;为他的最终命运做好准备。 然而&#xff0c;地球面临着世界末日&#xff0c;正义联盟需要他们的钢铁侠行动来拯救世界。 但是由于我们只有一个超人&#xff0c;您不能同时做这两个…

CSS实现垂直居中的5种方法

利用 CSS 来实现对象的垂直居中有许多不同的方法&#xff0c;比较难的是选择那个正确的方法。我下面说明一下我看到的好的方法和怎么来创建一个好的居中网站。 使用 CSS 实现垂直居中并不容易。有些方法在一些浏览器中无效。下面我们看一下使对象垂直集中的5种不同方法&#xf…

尝试使用jBPM Console NG(测试版)

大家好&#xff01; 这是关于jBPM Console NG的另一篇文章。 经过6个月的辛苦工作&#xff0c;我很高兴为开发者社区撰写这篇文章&#xff0c;以进行尝试。 在这篇文章中&#xff0c;我将解释如何从源代码构建应用程序。 这背后的主要思想是知道如何在测试过程中设置环境并修改…

php在window磁盘管理,Windows Server 2008R2设置磁盘阵列

RAID(独立硬盘冗余阵列)指用多个硬盘组成一个高性能、大容量的一个硬盘组合。独立硬盘冗余阵列(RAID, Redundant Array of Independent Disks)&#xff0c;旧称廉价磁盘冗余阵列(RedundantArray of Inexpensive Disks)&#xff0c;简称硬盘阵列。其基本思想就是把多个相对便宜的…

解决Error: ENOENT: no such file or directory, scandir 安装node-sass报错

新项目开发需要安装依赖&#xff0c;但是安装完之后通过gulp运行项目&#xff0c;产生了一下的报错&#xff1a; 解决方案是执行一些方法&#xff1a; npm rebuild node-sass可是有时就是网络问题导致上面命令安装失败&#xff0c;查下失败提示&#xff0c;有可能是&#xff1a…

系统讲解CSS,前端开发最神奇的技术,新手的你一定不能错过

前面小编带领大家重温了前端开发中最基本的HTML语言。如果你已经掌握了这门语言&#xff0c;那么恭喜你&#xff0c;可以去深入了解CSS技术了。CSS技术最主要的功能就是弥补HTML标记对在页面中显示外观的不足&#xff0c;对这些标记对的默认外观进行美化。从本文开始&#xff0…

JUnit和Mockito合作

这次&#xff0c;我想对测试框架Mockito进行概述。 毫无疑问&#xff0c;这是用于测试Java代码的最受欢迎的工具之一。 我已经对Mockito的竞争对手EasyMock进行了概述。 这篇文章将基于有关EasyMock的示例应用程序。 我的意思是代表咖啡机功能的类。 用Mockito准备测试 通常&a…

发现2017年最好的CSS框架

如今&#xff0c;无数的框架出现在定期而少数人喜欢自助&#xff0c;Foundation和angular.js主宰了整个世界的发展。CSS代表用于描述HTML&#xff08;或XML&#xff09;文档表示的样式表语言。一个框架被定义为一个包&#xff0c;它由一组结构化的文件和标准化代码&#xff08;…

go基础编程 day-2

Go的常亮 关键字&#xff1a; Const Go常亮的多个定义&#xff1a; // 定义常亮的关键字 const// 定义多个常亮 const(PI 3.14const1 "1"const2 2const3 3 ) 全局变量的声明与赋值&#xff1a; var (name "wyc"name1 1name2 2name3 3 ) 一般类型…

教你开发jQuery插件(转)

教你开发jQuery插件&#xff08;转&#xff09; 阅读目录 基本方法支持链式调用让插件接收参数面向对象的插件开发关于命名空间关于变量定义及命名压缩的好处工具GitHub Service Hook原文&#xff1a;http://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html 要说jQuery 最…

gulp 常用插件汇总

2017-07-26更新&#xff1a;图片压缩插件使用gulp-smushit&#xff0c;gulp-smushit压缩率比较大&#xff0c;gulp-imagemin 图片压缩插件压缩率不明显。 见下图压缩率&#xff1a; 1、gulp安装 参照gulp官网进行安装&#xff1a;http://www.gulpjs.com.cn/docs/getting-start…

Nmap介绍

1.Nmap介绍 Nmap用于列举网络主机清单、管理服务升级调度、监控主机或服务运行状况。Nmap可以检测目标机是否在线、端口开放情况、侦测运行的服务类型及版本信息、侦测操作系统与设备类型等信息。 1.1 Zenmap Zenmap是Nmap官方提供的图形界面&#xff0c;通常随Nmap的安装包发布…

SD/MMC相关寄存器的介绍

1.SD卡内部架构 在熟悉SD/MMC相关寄存器之前&#xff0c;我们先来看看SD卡的内部架构是怎么样的&#xff0c;如下图所示&#xff1a; 2.SD/MMC相关寄存器的介绍 从上图中总结出&#xff1a;SD卡内部有7个寄存器. 一、OCR,CID,CSD和SCR寄存器保存卡的配置信息; 二、RCA寄存器保存…

apche 禁止运行php,学习猿地-apache如何禁止执行php

apache禁止执行php的方法&#xff1a;首先新建一个“.htaccess”文件&#xff1b;然后复制代码内容“Order allow,deny”到“.htaccess”文件中&#xff1b;最后将该文件直接放到网站根目录里即可。apache禁止执行php的方法&#xff1a;第一种禁止上传目录运行php的方法如果你用…

Apache Camel 2.11发布

上周Apache Camel 2.11发布了。 这篇博客文章总结了最引人注目的新功能和改进。 有关详细说明&#xff0c;请参见Camel 2.11发行说明 。 1&#xff09;新组件 与往常一样&#xff0c;每个新发行版都包含许多新组件&#xff0c;这些组件由我们庞大的用户群贡献。 谢谢你们。 例…

linux分区满了,如何进行扩容

转自&#xff1a;https://blog.csdn.net/valage/article/details/73332147 图片中可以看到挂载点“/”的利用率移到100%&#xff0c;空间不够&#xff0c;所以要对其进行分区。 1. 先进入虚拟机设置里增大磁盘空间 注意&#xff1a;将25改成50&#xff0c;以扩大空间。这里…