李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
15.并发编程之synchronized原理进阶(一)
Leefs
2022-10-17 PM
1487℃
1条
[TOC] ### 一、轻量级锁 **轻量级锁的使用场景:**如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以 使用轻量级锁来优化。 轻量级锁对使用者是透明的,即语法仍然是 `synchronized` **示例** 假设有两个方法同步块,利用同一个对象加锁 ```java static final Object obj = new Object(); public static void method1() { synchronized (obj) { // 同步块 A method2(); } } public static void method2() { synchronized (obj) { // 同步块 B } } ``` **图解加锁过程** + 1、创建锁记录(Lock Record)对象,每个线程的**栈帧**都会包含一个锁记录的结构,内部可以存储锁定对象的`Mark Word` ![15.并发编程之synchronized原理进阶01.png](https://lilinchao.com/usr/uploads/2022/10/1869703945.png) + 2、让锁记录中`Object reference`指向锁对象,并尝试用cas替换Object的Mark Word,将Mark Word的值存入锁记录 ![15.并发编程之synchronized原理进阶02.png](https://lilinchao.com/usr/uploads/2022/10/228965150.png) > CAS(Compare and Swap):JDK提供的非阻塞原子性操作,它通过硬件保证了比较——更新操作的原子性。 + 3、如果`cas`(compare and swap)替换成功,对象头中存储了`锁记录地址和状态00`,表示由该线程给对象加锁 ![15.并发编程之synchronized原理进阶03.png](https://lilinchao.com/usr/uploads/2022/10/2026064643.png) **线程中的锁信息和锁对象中的Mark Word进行了互换** + 4、如果 cas 失败,有两种情况 + 如果是其它线程已经持有了该 Object 的**轻量级锁**,这时表明有竞争,进入锁膨胀过程; + 如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数(如下图所示)。 - 轻量级锁示例代码中t0执行`syn method1(obj)`,获得锁之后继续调用`syn method2(obj)`(多出来一个栈帧,见下图),两个加锁的`obj`是同一个对象,因此`CAS`失败 - 在图中的体现:对象头`lock record 地址 00`在调用`method1(obj)`改变了,指向的是第一个栈帧的锁记录,因此第二个栈帧会CAS失败 - `Lock Record`的null记录锁重入的计数,如上为1,再调用一次++ ![15.并发编程之synchronized原理进阶04.png](https://lilinchao.com/usr/uploads/2022/10/3895447534.png) + 5、当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一 ![15.并发编程之synchronized原理进阶05.png](https://lilinchao.com/usr/uploads/2022/10/4278993290.png) + 6、当退出synchronized代码块(解锁时) 锁记录的值不为null,这时使用cas将Mark Word的值恢复给对象头 - 成功,则解锁成功 - 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程 ### 二、锁膨胀 如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行`锁膨胀`,将轻量级锁变为重量级锁。 **示例** ```java static Object obj = new Object(); public static void method1() { synchronized (obj) { // 同步块 } } ``` + 1、当Thread-1进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁 ![15.并发编程之synchronized原理进阶06.png](https://lilinchao.com/usr/uploads/2022/10/1623551852.png) + 2、这时 Thread-1 加轻量级锁失败,进入锁膨胀流程 + 即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址; + 然后自己进入 Monitor 的 `EntryList BLOCKED`。 ![15.并发编程之synchronized原理进阶07.png](https://lilinchao.com/usr/uploads/2022/10/1253133339.png) + 3、当 Thread-0 退出同步块解锁时,使用 cas 将 Mark Word 的值恢复给对象头,失败。这时会进入`重量级解锁流程` - 即按照 Monitor 地址找到 Monitor 对象 - 设置 Owner 为 null - 唤醒 `EntryList` 中 BLOCKED 线程 ### 三、自旋优化 重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。 **自旋定义** 是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 #### 自旋重试成功的情况 | 线程1 (cpu1上) | 对象Mark | 线程2 (cpu2上) | | ----------------------- | ---------------------- | ----------------------- | | - | 10 (重量锁) | - | | 访问同步块,获取monitor | 10 (重量锁) 重量锁指针 | - | | 成功(加锁) | 10 (重量锁) 重量锁指针 | - | | 执行同步块 | 10 (重量锁) 重量锁指针 | - | | 执行同步块 | 10 (重量锁) 重量锁指针 | 访问同步块,获取monitor | | 执行同步块 | 10 (重量锁) 重量锁指针 | 自旋重试 | | 执行完毕 | 10 (重量锁) 重量锁指针 | 自旋重试 | | 成功(解锁) | 无锁 | 自旋重试 | | - | 10 (重量锁) 重量锁指针 | 成功(加锁) | | - | 10 (重量锁) 重量锁指针 | 执行同步块 | | - | ... | ... | *注意:自旋需要cpu资源,所以适合多核cpu* #### 自旋重试失败的情况 | 线程1 (cpu1上) | 对象Mark | 线程2 (cpu2上) | | ----------------------- | ---------------------- | ----------------------- | | - | 10 (重量锁) | - | | 访问同步块,获取monitor | 10 (重量锁) 重量锁指针 | - | | 成功(加锁) | 10 (重量锁) 重量锁指针 | - | | 执行同步块 | 10 (重量锁) 重量锁指针 | - | | 执行同步块 | 10 (重量锁) 重量锁指针 | 访问同步块,获取monitor | | 执行同步块 | 10 (重量锁) 重量锁指针 | 自旋重试 | | 执行同步块 | 10 (重量锁) 重量锁指针 | 自旋重试 | | 执行同步块 | 10 (重量锁) 重量锁指针 | 自旋重试 | | 执行同步块 | 10 (重量锁) 重量锁指针 | 阻塞 | | - | ... | ... | + 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。 + 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。 + Java 7 之后不能控制是否开启自旋功能 *附参考原文地址* 《*黑马程序员之并发编程*》
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2497.html
上一篇
14.并发编程之Monitor概念介绍
下一篇
16.并发编程之synchronized原理进阶(二)
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
序列化和反序列化
MyBatisX
Elastisearch
SpringBoot
Thymeleaf
线程池
Jquery
MyBatis-Plus
机器学习
查找
Python
Hadoop
nginx
Spark
Spark Core
哈希表
Spark RDD
Azkaban
Kibana
HDFS
栈
Nacos
Livy
BurpSuite
锁
FastDFS
散列
Quartz
Java工具类
JavaWeb
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭