JUC-Synchronized原理进阶

server/2024/11/13 8:06:25/

轻量级锁

轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化。轻量级锁对使用者是透明的,即语法仍然是 synchronized

假设有两个方法同步块,利用同一个对象加锁

java">static final Object obj = new Object();
public static void method1() {synchronized( obj ) {// 同步块 Amethod2();}
}
public static void method2() {synchronized( obj ) {// 同步块 B}
}
  • 创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的 Mark Word

  • 让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录

  • 如果 cas 替换成功,对象头中存储了锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下

  • 如果 cas 失败,有两种情况

    • 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程

    • 如果是自己执行了 synchronized 锁重入(从lock record的指向就可以知道是不是同一个线程),那么再添加一条 Lock Record 作为重入的计数

  • 当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一

  • 当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用cas将Mark Word的值恢复给锁的对象头

    • 成功,则解锁成功

    • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

 锁膨胀

如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有 竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

  • 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁,即对象的对象头的锁标志位是00,表示已是轻量级锁了。

  • 这时 Thread-1 加轻量级锁失败,进入锁膨胀流程

    • 即为 Object 对象申请 Monitor 锁,让 Object 的Mark Word指向重量级锁地址

    • 然后自己进入 Monitor 的 EntryList BLOCKED,进入阻塞队列等待。

  • 当 Thread-0 退出同步块解锁时,使用 cas 将 Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程

自旋优化

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步 块,释放了锁),这时当前线程就可以避免阻塞。如果发生阻塞,线程会从就绪态变为阻塞态,且会进行线程的上下文切换,耗费cpu资源

自旋重试成功的情况

线程1 ( core 1上)对象Mark线程2 ( core 2上)
-10(重量锁)-
访问同步块,获取monitor10(重量锁)重量锁指针-
成功(加锁)10(重量锁)重量锁指针-
执行同步块10(重量锁)重量锁指针-
执行同步块10(重量锁)重量锁指针访问同步块,获取 monitor
执行同步块10(重量锁)重量锁指针自旋重试
执行完毕10(重量锁)重量锁指针自旋重试
成功(解锁)01(无锁)自旋重试
-10(重量锁)重量锁指针成功(加锁)
-10(重量锁)重量锁指针执行同步块
-......

自旋重试失败的情况

线程1 ( core 1上)对象Mark线程2( core 2上)
-10(重量锁)-
访问同步块,获取monitor10(重量锁)重量锁指针-
成功(加锁)10(重量锁)重量锁指针-
执行同步块10(重量锁)重量锁指针-
执行同步块10(重量锁)重量锁指针访问同步块,获取monitor
执行同步块10(重量锁)重量锁指针自旋重试
执行同步块10(重量锁)重量锁指针自旋重试
执行同步块10(重量锁)重量锁指针自旋重试
执行同步块10(重量锁)重量锁指针阻塞
-......
  • 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势

  • 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会 高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。

  • Java 7 之后不能控制是否开启自旋功能

 

锁消除

JIT即时编译器会对代码进行优化,如果一个锁一直没有被其它线程竞争,只有一个线程持有,就会进行锁消除

java">@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations=3)
@Measurement(iterations=5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {static int x = 0;@Benchmarkpublic void a() throws Exception {x++;}@Benchmarkpublic void b() throws Exception {Object o = new Object();synchronized (o) {x++;}}
}

java -jar benchmarks.jar

Benchmark           Mode        Samples     Score       Score error     Units 
c.i.MyBenchmark.a   avgt        5           1.542           0.056       ns/op 
c.i.MyBenchmark.b   avgt        5           1.518           0.091       ns/op 

java -XX:-EliminateLocks -jar benchmarks.jar,禁止锁消除

Benchmark           Mode        Samples         Score       Score error     Units 
c.i.MyBenchmark.a   avgt        5               1.507       0.108           ns/op 
c.i.MyBenchmark.b   avgt        5               16.976      1.572           ns/op

http://www.ppmy.cn/server/106857.html

相关文章

安卓13 背光调节非线性问题处理,调节范围不正常问题

总纲 android13 rom 开发总纲说明 目录 1.前言 2.问题分析 3.代码修改 4.彩蛋 1.前言 我们看看现在的版本的亮度图 2.问题分析 当背光亮度设置为0%时,每次按下亮度增加键或者 input keyevent BRIGHTNESS_UP,亮度UI的增幅较大,首次按下后亮度平滑提升至大约55%,随后继…

Notion使用详解一基础教程

Notion是一款非常流行的笔记和协作工具,它结合了笔记、数据库、看板、日历等多种功能,非常适合个人知识管理、团队协作以及项目规划等。下面是一个基础教程,帮助你快速上手Notion。 1. 注册与登录 访问Notion官网注册完成后,登录…

3、Unity【基础】Resources资源场景动态加载

文章目录 一、Resources资源动态加载1、Unity中特殊文件夹1、工程路径获取2、Resources资源文件夹3、StreamingAssets流动资源文件夹4、persistentDataPath持久数据文件夹5、Plugins插件文件夹6、Editor编辑器文件夹7、默认资源文件夹StandardAssets 2、Resources同步加载1、Re…

Java CORS:跨越资源边界,探索跨域资源共享的无限可能

引言 随着前端技术的飞速发展,越来越多的应用程序开始使用多种不同的后端服务。这些服务往往部署在不同的域上,这就引发了跨域访问的问题。CORS作为一种解决跨域问题的有效机制,对于现代Web开发至关重要。本文将详细介绍如何在Java环境中配置…

DNS详解

DNS详解 DNS 是一个域名系统,它主要用于将人类容易记忆的域名转换成ip地址。 默认情况下,设备会自动从网络供应商获取DNS服务器地址,并使用DNS服务器对域名进行解析。 此外,你也可以手动设置DNS服务器,具体操作系统…

python自动化脚本:让工作自动化起来

Python是一种流行的编程语言,以其简洁和易读性而闻名。它提供了大量的库和模块,使其成为自动化各种任务的绝佳选择。 我们将探讨9个Python脚本及其代码,可以帮助您自动化各种任务并提高工作效率。无论您是开发人员、数据分析师还是只是想简化…

iPhone短信误删如何恢复,四种方法找回短信

在日常使用手机的过程中,我们可能会因为误操作或其他原因不小心删除了重要的短信。这些短信可能包含了工作沟通、家人关怀或朋友间的温馨对话,一旦丢失,难免让人感到焦虑和不安。不过,别担心,针对iPhone短信误删的问题…

【设计模式之原型模式——矩形原型】

原型模式的基本实现 创建⼀个抽象类或接⼝,声明⼀个克隆⽅法 clone 具体原型类去实现接口,重写克隆⽅法 客户端中实例化具体原型类的对象,并调⽤其克隆⽅法来(赋给)创建新的对象。 什么时候实现原型模式 ?…