【Java】锁机制
参考
https://blog.csdn.net/varyall/article/details/79698145
《深入理解Java虚拟机》
锁状态:无锁、偏向锁、轻量级锁、重量级锁(具体是何种状态,取决于竞争情况)
这三个只是代表锁的状态。而非动作。
首先了解为什么锁的是对象
HotSpot虚拟机的对象的内存分布主要分为三个部分:
对象头(分为两部分)
第一部分用于存储对象自身的运行时数据,如哈希吗,GC分代年龄(又称为Mark Word)
第二部分用于存储指向方法区对象类型数据的指针。即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。
实例数据(真正存储有效信息)
对齐填充(非必须,仅起着占位符的作用)
HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,即对象大小必须是8字节的整数倍。而对象头正好是8字节的整数倍,而对象实例数据部分没有对齐,需要通过对齐填充来补全。
虚拟机通过Mark Word的信息来区分锁的状态:
Epoch:偏向时间戳
轻量级锁、重量级锁、偏向锁
这四种状态都不是Java语言中的锁,而是Jvm为了提高锁的获取与释放效率而做的优化
四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级。
轻量级锁:本意是在没有多线程竞争的前提下,通过CAS操作而避免了使用互斥量的开销。
在只有两个线程的情况下,提高性能。
偏向锁:大多数情况下,锁不存在多线程竞争,而是总是由同一线程多次获得时,为了使线程获得锁的代价更低而引入了偏向锁。提高了带有同步,但无竞争的程序性能。
在只有一个线程的情况下,进一步提高性能。
轻量级锁的原理
虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间。
将还没有被锁定的同步对象的对象头中的Mark Word拷贝到锁记录空间中
拷贝成功后,使用CAS操作将对象头中的Mark Word修改为一个指针(指向此线程的锁记录空间)
如果CAS操作成功,这个线程就拥有了该对象锁,并将锁标志“01”改为“00”,即表示此对象处于轻量级锁的状态。此时,如果有另一个线程竞争轻量级锁,则会发生自旋。
如果失败了,说明这个Mark Word已经被别的线程拿走了,如果存在两条以上的线程争夺同一个锁,那么轻量级锁就会膨胀为重量级锁。锁标志则被修改为“10”。膨胀之后,不可逆转。变成重量级锁之后,其他竞争失败的线程不会自旋,而是阻塞。(大量线程自旋,极大浪费CPU)
解锁过程:
依然是CAS操作,将上面被替换的Mark Word再替换回对象头中。
替换失败,说明轻量级锁已经膨胀成了重量级锁,所以CAS操作替换失败,此时,一定有其他线程在抢夺锁的过程中被阻塞,这时候,释放锁的同事,唤醒被挂起的线程。
偏向锁
轻量级锁的获取和释放都依赖于CAS原子操作,而偏向锁是在无竞争的情况下,连CAS操作都不做了。只有在需要置换ThreadID的时候,执行一次CAS原子操作。在只有一个线程执行同步块的情况下,进一步提高性能。
偏向锁的获取过程:
在对象第一次被线程获取的时候,查看Mark Word中偏向锁标志是否为1,锁标志是否为01。是否已经确定Thread ID,如果已经确定,则不需要CAS操作,直接执行同步代码块。
如果没有确定Thread ID,则通过CAS操作竞争锁,成功:则将Mark Word中的Thread ID设置为当前线程ID,然后执行同步代码块
如果竞争失败:则说明有线程一同竞争,偏向锁会升级为轻量级锁。竞争失败的线程自旋。
偏向锁的释放:
如果没有竞争,线程不会主动释放偏向锁,只有在有竞争的情况下,偏向锁会被撤销,提升为轻量级锁。
三种锁的转化
三种锁对比
自旋锁
线程的阻塞,挂起,唤醒,都会给系统性能带来压力。
有时候线程只需要等待很短的时间,这个时间将线程挂机,就很不值得。
所以自旋技术:让线程执行一个忙循环(自旋),稍等一下。这就是自旋锁。
-XX:+UseSpinning开启自旋锁。JDK1.6之后,默认开启。
自旋锁的效果:避免了线程切换的开销,但是在自旋过程中要占用CPU。如果锁被占用很短,自旋时间很短,自旋等待的效果就会很好,反之,如果锁占用时间长,自旋就会很浪费资源。
JDK1.6之前自旋默认循环次数为10次。可以使用参数-XX:PreBlockSpin来修改。
JDK1.6之后,次数不在固定,可以自适应,由同一个锁的前一个自旋时间来决定。比如:上一个等待此锁的线程自旋了多久,这次系统就会让此线程自旋多久。
锁消除
对于堆内存中永远不会逃逸,不会被其他线程访问到的变量,就认为是线程私有的,无需同步加锁。
虚拟机即时编译器在运行时,如果检测到不可能存在共享数据竞争的锁,说明这个锁无意义,就会自动进行消除这个锁。这就是锁消除。
// 看起来没有同步的方法
public String concatString(String str1,String str2,String str3){
return str1 + str2 + str3;
}
// 在JDK1.5之前,会自动转化为StringBuffer进行append()操作
public String concatString(String str1,String str2,String str3){
StringBuffer sb = new StringBuffer();
sb.append(str1);
sb.append(str2);
sb.append(str3);
return sb.toString();
}
锁粗化
原则上,将同步块的作用范围限制的尽量小,在多线程中的互斥同步的操作数量就会尽可能的小,如果存在锁竞争,那等待锁的线程也能尽快拿到锁。
大部分情况下,这是正确的。但是,如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作在循环体中,即使没有竞争,频繁互斥同步也会导致不必要的性能损耗。
锁粗化:如果虚拟机检测到有这样一连串零碎操作(如上面的代码)都对同一个对象加锁,将会把加锁同步的范围扩大到整个操作序列的外部。
【Java】锁机制的更多相关文章
- 转 : 深入解析Java锁机制
深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...
- Java 锁机制总结
锁的种类 独享锁 VS 共享锁 独享锁:锁只能被一个线程持有(synchronized) 共享锁:锁可以被多个程序所持有(读写锁) 乐观锁 VS 悲观锁 乐观锁:每次去拿数据的时候都乐观地认为别人不会 ...
- java锁机制的面试题
java锁机制的面试题 1.ABA问题 2.CAS乐观锁 3.synchronize实现原理 4.synchronize与lock的区别 5.volatile实现原理 6.乐观锁的业务场景及实现方式 ...
- Java锁机制深入理解
Java锁机制 背景知识 指令流水线 CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际上是不断地取出指令.分析指令.执行指令的过程. 几乎所有的冯•诺伊曼型计算机的CPU,其工 ...
- java锁机制
2.4 锁机制 临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作.这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临 ...
- Java锁机制了解一下
前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchro ...
- JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,
如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...
- JAVA锁机制(上)
在实际开发中经常会用到多线程协作来处理问题,锁是处理线程安全不可缺少的机制.在JAVA中可以通过至少三种方式来实现线程锁. 1. synchronized修饰符,这种锁机制是虚拟机实现的一种锁. 2 ...
- java 锁机制(synchronized 与 Lock)
在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制. synchronized是java中的一个关键字,也就是说是java内置的 ...
- 【面试专栏】JAVA锁机制
1. 悲观锁 / 乐观锁 在Java和数据库中都存在悲观锁和乐观锁的应用.Mysql锁机制中的悲观锁和乐观锁请查看: Mysql锁机制--悲观锁和乐观锁 悲观锁:在获得数据时先加锁,只到数 ...
随机推荐
- 文件数据库sqlite3 C++ 线程安全和并发
转载:https://www.cnblogs.com/feng9exe/p/10682567.html(线程安全和并发) 转载:https://juejin.im/post/5b7d8522e51d4 ...
- 源码包的三个参数make-configure-make install解释
1.configure 这一步一般用来生成 Makefile,为下一步的编译做准备,你可以通过在 configure 后加上参数来对安装进行控制,比如代码: ./configure --prefix= ...
- ISO/IEC 9899:2011 条款6.10——预处理指示符
6.10 预处理指示符 语法 1.preprocessing-file: groupopt group: group-part group group-part group-part: if-s ...
- linux环境中,openssl升级及openresty中nginx基于新版本openssl重新编译
需求说明: 最近在对系统进行安全扫描的时候,出现了openssl版本的问题,建议对openssl版本进行升级,在此记录下升级过程. 环境说明: 操作系统:RHEL 6.6 升级操作过程: 1.下载最新 ...
- MSYS2 编译环境配置
博客转载自:https://medium.com/@fzubair/carnd-term2-gcc-setup-on-windows-287e1fd36845 2018/6/2 — mingw32/c ...
- Swift编码总结5
1.UIWindow属性: 1>.- (void)becomeKeyWindow; // override point for sub ...
- 【视频开发】四大图像库:OpenCV/FreeImage/CImg/CxImage
1.对OpenCV 的印象:功能十分的强大,而且支持目前先进的图像处理技术,体系十分完善,操作手册很详细,手册首先给大家补计算机视觉的知识,几乎涵盖了近10年内的主流算法: 然后将图像格式和矩阵运算, ...
- VMware的包格式vmdk转换为virtualBox的ova
使用winxp的vmdk作为案例 1 使用vmvare导入vmdk的winxp,点击文件---->导出为ovf 2 找到生成的ovf文件 3 打开virtualBox 管理---->导入虚 ...
- Qt qml调试,qml性能分析和优化工具
QML语言为qt推出的用于界面编程的语言. 1)如何在qt creator中进行调试qml: 以Qt Creator 4.6.2为例: 在qt creator的debug模式下,可以直接在qml中打断 ...
- 通过元类创建一个Python类
通过元类创建一个Python类 最开始学pytohn的时候我们这样定义类 class ClassName: pass 当熟悉了元类的概念之后我们还可以这样创建 ClassName = type(&qu ...