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,一经查实,立即删除!

相关文章

使用镜像源安装EASY_INSTALL和PIP教程

使用easy_install和pip可以让python的模块的安装和管理变得非常方便。我一般在新的Linux系统上,先easy_install pip然后就用pip安装其他的模块了。 不过,在国内用官方的pypi源(https://pypi.python.org/simple)一般比较慢&#xf…

Flexbox布局

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

臭名昭著的Java错误和陷阱

在2000年,我上大学,濒临选择一种语言来发展自己的职业。 Java尚未成为主流,但很受人们欢迎。 与静态html页面相比,小应用程序(尚未破碎)花哨且富有光泽。 Swing不是构建桌面应用程序的不错选择。 J2EE越来越…

php oracle 配置,关于php:为Windows 64位配置Oracle OCI8

我一直在为Windows配置oracle oci8时遇到问题我正在使用Windows 7 64位,xampp v3.2.2,php 5.6.15和oracle g11 Express我尝试了以下步骤,但是在运行phpinfo时找不到oci软件包:我同时下载了Instant Client Package-Basic和Instant …

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 …

解决android studio引用远程仓库下载慢(JCenter下载慢)

原文&#xff1a;https://blog.csdn.net/linglingchenchen/article/details/62236723 第一种方法 使用开源中国的maven库 阿里云的(速度飞快)&#xff1a;http://maven.aliyun.com/nexus/content/groups/public/ 替换项目根目录下build.gradle中的 repositories {jcenter()} 为…

Java中的工厂方法模式

在我以前关于模板方法模式的文章中 &#xff0c;我展示了如何利用lambda表达式和默认方法 。 在本文中&#xff0c;我将探讨工厂方法模式&#xff0c;并了解如何利用方法引用&#xff0c;这是Java 8中与lambda表达式一起添加的另一项功能。 让我们考虑一个Vehicle接口及其两个…

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

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

MVP数据请求recycview展示

M层 public class Mod {public void shuju(final Modlister modlister){ OkHttpClient okHttpClientnew OkHttpClient();final Request requestnew Request.Builder().url("http://v.juhe.cn/weixin/query?key55010ae808217e996bbf414bbdad843c").build(); Call …

impress.js

介绍一下 impress.js是一个非常炫酷的幻灯片展示框架&#xff0c;依靠CSS3技术。 impress.js使用起来非常简单&#xff0c;下面就来简单介绍一下其用法。 Start 首先&#xff0c;当然要引入impress.js。 在div标签设置id为impress&#xff08;不要求一定是div&#xff09;…

Spring MVC:表单处理卷。 1个

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

用php打出前一天的时间格式,[php]用PHP打印出前一天的时间格式

echo date("Y-m-d H:i:s",strtotime("-1 day"));例子echo(strtotime("now"));echo(strtotime("3 October 2005"));echo(strtotime("5 hours"));echo(strtotime("1 week"));echo(strtotime("1 week 3 days 7 …

《Python学习之路 -- 字符串的方法》

在前面已经提到Python中的字符串了&#xff0c;本文来列举介绍字符串的方法&#xff0c;我将字符串的方法分为以下几类&#xff1a; ①查询方法 str.find(target,startNone,endNone) 该方法用于查询字符串str中是否含有target&#xff0c;如果有&#xff0c;则返回target在这个…

Java监视器绑定的超人

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

C语言第一次实验报告

一&#xff0e;实验题目&#xff0c;设计思路&#xff0c;实现方法 第四次 计算分段函数和循环NEW 4-3 求N分之一序列前N项和 4-6 输出华氏-摄氏温度转换表 第四次 分支循环 加强版 4-2-7 装睡 设计思路&#xff1a;4-3开始需要计算大量式子&#xff0c;因此利用到相对便捷的…

php充值卡,ecshop 充值卡功能_懒人程序

/*显示页面的action列表*/$ui_arrarray(register,login,profile,... 增加cz然后在下面代码处加入if ($actioncz)//冲值{ if($_GET[a]add){ if (empty($_SESSION[user_id])){ show_message(该用户不存在。,返回上一页,user.php?actcz);} else{ $kstrtoupper(substr($_POST[k],0…

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

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

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

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

三个实用的提升网页性能技巧

1、display属性设置 先将元素设为 display: none&#xff08;需要1次重排和重绘&#xff09;&#xff0c;然后对这个节点进行100次操作&#xff0c;最后再恢复显示&#xff08;需要1次重排和重绘&#xff09;。这样一来&#xff0c;你就用两次重新渲染&#xff0c;取代了可能高…

mysq在某一刻同时获取主从库的位置点

在从库进行锁表操作flush table with read lock&#xff0c; 通过show slave status\G 获取对应主库的位置点&#xff1a; show slave status\G*************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host…