参考

https://blog.csdn.net/varyall/article/details/79698145

《深入理解Java虚拟机》

锁状态:无锁、偏向锁、轻量级锁、重量级锁(具体是何种状态,取决于竞争情况

这三个只是代表锁的状态。而非动作。

首先了解为什么锁的是对象

HotSpot虚拟机的对象的内存分布主要分为三个部分:

  • 对象头(分为两部分)

    1. 第一部分用于存储对象自身的运行时数据,如哈希吗,GC分代年龄(又称为Mark Word)

    2. 第二部分用于存储指向方法区对象类型数据的指针。即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。

  • 实例数据(真正存储有效信息)

  • 对齐填充(非必须,仅起着占位符的作用)

    HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,即对象大小必须是8字节的整数倍。而对象头正好是8字节的整数倍,而对象实例数据部分没有对齐,需要通过对齐填充来补全。

虚拟机通过Mark Word的信息来区分锁的状态:

Epoch:偏向时间戳

轻量级锁、重量级锁、偏向锁

这四种状态都不是Java语言中的锁,而是Jvm为了提高锁的获取与释放效率而做的优化

四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级。

  • 轻量级锁:本意是在没有多线程竞争的前提下,通过CAS操作而避免了使用互斥量的开销。

    在只有两个线程的情况下,提高性能。

  • 偏向锁:大多数情况下,锁不存在多线程竞争,而是总是由同一线程多次获得时,为了使线程获得锁的代价更低而引入了偏向锁。提高了带有同步,但无竞争的程序性能。

    在只有一个线程的情况下,进一步提高性能。

轻量级锁的原理

  1. 虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间。

  2. 将还没有被锁定的同步对象的对象头中的Mark Word拷贝到锁记录空间中

  3. 拷贝成功后,使用CAS操作将对象头中的Mark Word修改为一个指针(指向此线程的锁记录空间)

  4. 如果CAS操作成功,这个线程就拥有了该对象锁,并将锁标志“01”改为“00”,即表示此对象处于轻量级锁的状态。此时,如果有另一个线程竞争轻量级锁,则会发生自旋。

  5. 如果失败了,说明这个Mark Word已经被别的线程拿走了,如果存在两条以上的线程争夺同一个锁,那么轻量级锁就会膨胀为重量级锁。锁标志则被修改为“10”。膨胀之后,不可逆转。变成重量级锁之后,其他竞争失败的线程不会自旋,而是阻塞。(大量线程自旋,极大浪费CPU)

解锁过程:

  1. 依然是CAS操作,将上面被替换的Mark Word再替换回对象头中。

  2. 替换失败,说明轻量级锁已经膨胀成了重量级锁,所以CAS操作替换失败,此时,一定有其他线程在抢夺锁的过程中被阻塞,这时候,释放锁的同事,唤醒被挂起的线程。

偏向锁

轻量级锁的获取和释放都依赖于CAS原子操作,而偏向锁是在无竞争的情况下,连CAS操作都不做了。只有在需要置换ThreadID的时候,执行一次CAS原子操作。在只有一个线程执行同步块的情况下,进一步提高性能。

偏向锁的获取过程:

  1. 在对象第一次被线程获取的时候,查看Mark Word中偏向锁标志是否为1,锁标志是否为01。是否已经确定Thread ID,如果已经确定,则不需要CAS操作,直接执行同步代码块。

  2. 如果没有确定Thread ID,则通过CAS操作竞争锁,成功:则将Mark Word中的Thread ID设置为当前线程ID,然后执行同步代码块

  3. 如果竞争失败:则说明有线程一同竞争,偏向锁会升级为轻量级锁。竞争失败的线程自旋。

偏向锁的释放:

如果没有竞争,线程不会主动释放偏向锁,只有在有竞争的情况下,偏向锁会被撤销,提升为轻量级锁。

三种锁的转化

三种锁对比

自旋锁

线程的阻塞,挂起,唤醒,都会给系统性能带来压力。

有时候线程只需要等待很短的时间,这个时间将线程挂机,就很不值得。

所以自旋技术:让线程执行一个忙循环(自旋),稍等一下。这就是自旋锁。

-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】锁机制的更多相关文章

  1. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

  2. Java 锁机制总结

    锁的种类 独享锁 VS 共享锁 独享锁:锁只能被一个线程持有(synchronized) 共享锁:锁可以被多个程序所持有(读写锁) 乐观锁 VS 悲观锁 乐观锁:每次去拿数据的时候都乐观地认为别人不会 ...

  3. java锁机制的面试题

    java锁机制的面试题 1.ABA问题 2.CAS乐观锁 3.synchronize实现原理 4.synchronize与lock的区别 5.volatile实现原理 6.乐观锁的业务场景及实现方式 ...

  4. Java锁机制深入理解

    Java锁机制 背景知识 指令流水线 ​ CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际上是不断地取出指令.分析指令.执行指令的过程. ​ 几乎所有的冯•诺伊曼型计算机的CPU,其工 ...

  5. java锁机制

    2.4 锁机制        临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作.这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临 ...

  6. Java锁机制了解一下

    前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchro ...

  7. JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,

    如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...

  8. JAVA锁机制(上)

    在实际开发中经常会用到多线程协作来处理问题,锁是处理线程安全不可缺少的机制.在JAVA中可以通过至少三种方式来实现线程锁. 1.  synchronized修饰符,这种锁机制是虚拟机实现的一种锁. 2 ...

  9. java 锁机制(synchronized 与 Lock)

    在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制. synchronized是java中的一个关键字,也就是说是java内置的 ...

  10. 【面试专栏】JAVA锁机制

    1. 悲观锁 / 乐观锁   在Java和数据库中都存在悲观锁和乐观锁的应用.Mysql锁机制中的悲观锁和乐观锁请查看:   Mysql锁机制--悲观锁和乐观锁   悲观锁:在获得数据时先加锁,只到数 ...

随机推荐

  1. t420 win7 硬盘安装ubuntu 10.04 LTS 备忘

    http://zhangwen.sinaapp.com/?p=5 t420 win7 硬盘安装ubuntu 10.04 LTS 备忘 发表于 2011 年 10 月 25 日 对ubuntu的新版没有 ...

  2. 人物-IT-李想:百科

    ylbtech-人物-IT-李想:百科 李想,1981年10月出生于河北石家庄,80后企业家代表人物.曾先后创立泡泡网.汽车之家,现任车和家创始人及CEO. 1998年还在上高中的李想就开始做个人网站 ...

  3. nginx反向代理结合apache和php的配置示例

    .前端nginx主配置文件 # cat nginx.conf worker_processes ; #pid logs/nginx.pid; pid /data/www/logs/nginx.pid; ...

  4. 【技术】Arduino PID自整定库

    最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助.作者Brett Beaure ...

  5. [LeetCode] 240. Search a 2D Matrix II 搜索一个二维矩阵 II

    Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...

  6. C#不区分大小写的字符串替换(Replace)函数

    在.NET中,不调用C++/CLI,进行字符串替换有好几种方法: 1.最常用的,就是String实例.Replace(),但这个不能忽略大小写. 2.System.Text.Regex(Regular ...

  7. 考前最后的感叹:CSP2019 Bless All! & AFO

    因为没有退路,所以勇往直前. ----来自高二老年选手小蒟蒻XY Upd:凉凉了,你们都稳了...我看来是超不过准考证号了qwq[大哭] Upd:来自联考txdy的神仙gcz Upd:久远的回忆:会不 ...

  8. Github首次使用教程(本地新建项目并同步到Github远程仓库)

    网上关于Github的教程很多且有点乱,自己亲自躺坑实践,现分享出来给将要入坑的小伙伴. 主要步骤: 创建Github帐号,登录,新建仓库(远程仓库) 下载安装Git,git bash配置及简单使用( ...

  9. 算法浅谈之DP悬线法

    悬线法 用途 解决给定矩阵中满足条件的最大子矩阵 做法 用一条线(横竖貌似都行)左右移动直到不满足约束条件或者到达边界 定义 \(left[i][j]\):代表从\((i,j)\)能到达的最左位置 \ ...

  10. 《算法 - Lru算法》

    一:概述 - LRU 用于管理缓存策略,其本身在 Linux/Redis/Mysql 中均有实现.只是实现方式不尽相同. - LRU 算法[Least recently used(最近最少使用)] - ...