JAVA并发之锁获取步骤及锁优化
在另外的两篇文章中先后介绍了轻量级同步关键字volatile和重量级锁关键字synchronized,这两个关键字是Java语言中进行线程同步的基本方式(当然还有ReentrenLock等显式锁方式)。本文将就Java虚拟机针对同步原语做的一些锁优化进行简单的介绍,同时基于JDK 1.6将这些锁优化措施设置为默认值,对锁的获取流程进行图示。
锁优化
1、自旋锁与自适应自旋
我们知道,互斥同步时候,对性能影响最大的是阻塞的实现,挂起线程和恢复线程都需要陷入内核态去完成,而频繁的用户态内核态切换势必会造成频繁的上下文切换从而影响了性能,所以自旋锁就出现了,如果共享资源的占用时间不是很长的话,想要进入临界区的线程完全可以不放弃处理器的执行时间,只要执行一个忙循环即可。但是资源的占用时间也不是我们可以凭空想象的,所以我们可以通过-XX:PreBlockSpin参数来修改自旋次数,如果忙循环的循环次数大于了设置的自旋次数,那么还是执行原来的方式挂起线程。
2、锁消除
锁消除是指虚拟机在编译器运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。比如在判断一段代码中,堆上的数据都不会逃逸出去从而被其他的线程访问到,那么可以认为他们是线程私有的,同步加锁自然无须进行。
3、所粗化
我们熟悉的是减小锁的粒度如锁分解和锁分段等优化措施,但有的时候,如果一系列的连续操作对都对一个对象反复加锁和减锁,甚至枷锁操作是出现在循环体中的,那么即使没有线程竞争,频繁的进行互斥同步操作也会导致不必要的性能损耗。(注意:这项优化措施似乎在有了偏向锁和轻量级锁后就不太需要了---个人感觉)
4、轻量级锁
轻量级锁匙JDK 1.6之后新加入的新型锁机制,它的实现和后面的偏向锁的实现都需要Java对象头MarkWord信息的帮助

对象头中的MarkWord信息是可以复用的,复用的具体情况如上图所示。在代码进入同步块时,如果此对象没有被锁定(锁标志位“01”状态),虚拟机将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的MarkWord拷贝(官方把这份拷贝加了一个Displaced前缀,即Displaced Mark Word)

然后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,如果这个更新成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后2bit)将转变为“00”,即表示此对象处于轻量级锁定状态

如果更新失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果只说明了当前线程已经拥有了这个对象的锁,那就可以直接进入同步块执行,否则则说明这个锁对象已经被其他线程抢占了。如果有两个不同的线程抢占同一个锁,那么轻量级锁就会膨胀为重量级锁(互斥同步)
5、偏向锁
偏向锁的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。轻量级锁匙在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS也不要了。它的实现原理和轻量级锁一样,也是和对象头的Mark Word相关。
结合所有的锁优化步骤,那么代码执行锁获取操作的流程如下图所示

参考资料:《深入理解Java虚拟机》---周志明
JAVA并发之锁获取步骤及锁优化的更多相关文章
- Java并发之彻底搞懂偏向锁升级为轻量级锁
网上有许多讲偏向锁,轻量级锁的文章,但对偏向锁如何升级讲的不够明白,有些文章还相互矛盾,经过对jvm源码(biasedLocking.cpp)的仔细分析和追踪,基本升级过程有了一个清晰的过程,现将升级 ...
- Java多线程(五) —— 线程并发库之锁机制
参考文献: http://www.blogjava.net/xylz/archive/2010/07/08/325587.html 一.Lock与ReentrantLock 前面的章节主要谈谈原子操作 ...
- JVM锁简介:偏向锁、轻量级锁和重量级锁
转自:https://www.aimoon.site/blog/2018/05/21/biased-locking/ 比较复杂,简略见另一篇:https://www.cnblogs.com/twohe ...
- 001-多线程-锁-架构【同步锁、JUC锁】
一.概述 Java中的锁,可以分为"同步锁"和"JUC包中的锁". 1.1.同步锁 即通过synchronized关键字来进行同步,实现对竞争资源的互斥访问的锁 ...
- java并发之线程同步(synchronized和锁机制)
使用synchronized实现同步方法 使用非依赖属性实现同步 在同步块中使用条件(wait(),notify(),notifyAll()) 使用锁实现同步 使用读写锁实现同步数据访问 修改锁的公平 ...
- Java并发之显式锁和隐式锁的区别
Java并发之显式锁和隐式锁的区别 在面试的过程中有可能会问到:在Java并发编程中,锁有两种实现:使用隐式锁和使用显示锁分别是什么?两者的区别是什么?所谓的显式锁和隐式锁的区别也就是说说Synchr ...
- Java并发之锁升级:无锁->偏向锁->轻量级锁->重量级锁
Java并发之锁升级:无锁->偏向锁->轻量级锁->重量级锁 对象头markword 在lock_bits为01的大前提下,只有当是否偏向锁位值为1的时候,才表明当前对象处于偏向锁定 ...
- 转 Java并发之锁的升级
说明:本文大部分内容来自<并发编程的艺术>,再加上自己网络整理和理解 以下内容来自<java并发编程的艺术>作者:方鹏飞 魏鹏 程晓明 在多线程并发编程中synchronize ...
- Java并发之(3):锁
锁是并发编程中的重要概念,用来控制多个线程对同一资源的并发访问,在支持并发的编程语言中都有体现,比如c++ python等.本文主要讲解Java中的锁,或者说是重入锁.之所以这么说是因为在Java中, ...
随机推荐
- 测试代码的执行时间魔法方法%time和%timeit
对于规模更大.运行时间更长的数据分析应用程序,你可能会希望测试一下各个部分或函数调用或语句的执行时间.你可能会希望了解某个复杂计算过程中到底是哪些函数占用的时间最多.幸运的是,在开发和测试代码的过程中 ...
- 使C# WebApi返回Json
找到Global.asax文件,在Application_Start()方法中添加一句: protected void Application_Start() { AreaRegistration.R ...
- hdu 1556 Color the ball(区间更新,单点求值)
Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- [转载] linux下tar命令解压到指定的目录
参考 http://blog.sina.com.cn/s/blog_62449fcf0100nfar.html linux下tar命令解压到指定的目录 : #tar zxvf /bbs.tar.z ...
- Python--day28--面试题
- 四叶草(css)
<!DOCTYPE html><html><head> <meta charset="utf-8"> <style> . ...
- P1098 方程解的个数
题目描述 给出一个正整数N,请你求出x+y+z=N这个方程的正整数解的组数(1<=x<=y<=z<1000).其中,1<=x<=y<=z<=N . 输入 ...
- linux 延后执行
设备驱动常常需要延后一段时间执行一个特定片段的代码, 常常允许硬件完成某个任务. 在这一节我们涉及许多不同的技术来获得延后. 每种情况的环境决定了使用哪种技术最好; 我们全都仔细检查它们, 并且指出每 ...
- 原生js重写each方法
js原生有个for-each方法,但是只能遍历数组不能遍历对象; jq有个$.each倒是可以遍历数组和对象,但是项目中如果不想用jq呢,我们就用原生来写一个吧. [12,23,34].forEach ...
- read、write 与recv、send区别 gethostname
recv相对于read有什么区别呢? 其实它跟read函数功能一样,都可以从套接口缓冲区sockfd中取数据到buf,但是recv仅仅只能够用于套接口IO,并不能用于文件IO以及其它的IO,而read ...