synchronized关键字的使用和原理

synchronized关键字的使用和原理

synchronized:对象锁,保证了临界区内代码的原子性,采用互斥的方式让同一时刻至多只有一个线程能持有对象锁,其它线程获取这个对象锁时会阻塞,保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换。

1、使用方式:

锁对象:理论上可以是任意的唯一对象

package com.jtc.fe;
 
public class synchronizedDemo {
 
    // 同步静态方法 ——> 锁住class类对象 ——> 全局唯一
    synchronized public static void func1(){}
 	// 同步方法 ——> 锁住实例对象 ——> 每个实例都可以当锁
    synchronized public void func3(){}
    
    // 同步代码块
    public void func2(){
        // ——> 锁住class类对象 ——> 全局唯一
        synchronized (synchronizedDemo.class){}
    }
    public void func4(){
        // ——> 锁住实例对象 ——> 每个实例都可以当锁
        synchronized (this){}
    }
    public void func5(){
        // ——> 锁住字符串"123"
        synchronized ("123"){}
    }
}

注意:

synchronized 修饰的方法的不具备继承性,所以子类是线程不安全的,如果子类的方法也被 synchronized 修饰,两个锁对象其实是一把锁,而且是子类对象作为锁

2、锁原理

Java的对象由三部分组成:对象头 + 实例数据 + 对齐填充。

2.1、对象头

64位对象头由Mark Word、Class Pointer两部分组成,如果对象是数组,则还要加上数组长度,即三部分组成。在这里插入图片描述

Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。由于对象需要存储的运行时数据很多,但64位虚拟机给它的空间只有64位Bitmap,所以Mark Word被设计成一个有着动态定义的数据结构,即不同的锁状态存储内容不同。
在这里插入图片描述

如果要查看内存结构图,需要引入依赖:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
import com.example.demo.User;
import org.openjdk.jol.info.ClassLayout;

public class p6 {
    static User user = new User();
    static User[] users = new User[10];
    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(user).toPrintable());
        System.out.println("*******************************************");
        System.out.println(ClassLayout.parseInstance(users).toPrintable());
    }
}

在这里插入图片描述

Mark Word由64位8个字节组成。Class Pointer由64位8个字节组成,但我们使用的64位 JVM会默认使用选项 +UseCompressedOops 开启指针压缩,将指针压缩至32位。即上面截图中的Class Pointer为4个字节32位。

如果在打印对象头前调用HashCode方法,则会变成如下:
在这里插入图片描述

从MarkWord的结构可以看出,在无锁阶段内存分布与上图是一一对应的,HashCode也是一一对应的。

2.2、锁升级

synchronized 是可重入、不公平的重量级锁,所以可以对其进行优化。

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁	// 随着竞争的增加,只能锁升级,不能降级
其中重量级锁还需要Monitor对象配合使用

偏向锁

偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程之后重新获取该锁不再需要同步操作:当锁对象第一次被线程获得的时候进入偏向状态,使用 CAS 操作将线程 ID 记录到 Mark Word。

轻量级锁

当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向(Revoke Bias)后恢复到未锁定或轻量级锁状态。一个对象有多个线程要加锁,但加锁的时间是错开的(没有竞争),可以使用轻量级锁来优化,轻量级锁对使用者是透明的(不可见)。

重量级锁

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

在重量级锁阶段,每个 Java 对象都可以关联一个 Monitor 对象,Monitor 也是 class,其实例存储在堆中,如果使用 synchronized 给对象上锁之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针,这就是重量级锁。
在这里插入图片描述

工作流程:

  • 开始时 Monitor 中 Owner 为 null

  • 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor 中只能有一个 Owner,obj 对象的 Mark Word 指向 Monitor,把对象原有的 MarkWord 存入线程栈中的锁记录

  • 在 Thread-2 上锁的过程,Thread-3、Thread-4、Thread-5 也执行 synchronized(obj),就会进入 EntryList BLOCKED(双向链表)

  • Thread-2 执行完同步代码块的内容,根据 obj 对象头中 Monitor 地址寻找,设置 Owner 为空,把线程栈的锁记录中的对象头的值设置回 MarkWord

  • 唤醒 EntryList 中等待的线程来竞争锁,竞争是非公平的,如果这时有新的线程想要获取锁,可能直接就抢占到了,阻塞队列的线程就会继续阻塞

  • WaitSet 中的 Thread-0,是以前获得过锁,但条件不满足进入 WAITING 状态的线程(wait-notify 机制)

2.3、代码字节码

修饰代码段时:

public static void main(String[] args) {
    Object lock = new Object();
    synchronized (lock) {
        System.out.println("ok");
    }
}
0: 	new				#2		// new Object
3: 	dup
4: 	invokespecial 	#1 		
7: 	astore_1 				
8: 	aload_1					
9: 	dup						
10: astore_2 				
11: monitorenter 			// 【将 lock对象 MarkWord 置为 Monitor 指针】
12: getstatic 		#3		
15: ldc 			#4		
17: invokevirtual 	#5 		
20: aload_2 				
21: monitorexit 			// 【将 lock对象 MarkWord 重置, 唤醒 EntryList】
22: goto 30
25: astore_3 				
26: aload_2 				
27: monitorexit 			// 【将 lock对象 MarkWord 重置, 唤醒 EntryList】
28: aload_3
29: athrow
30: return
Exception table:
    from to target type
      12 22 25 		any
      25 28 25 		any
LineNumberTable: ...
LocalVariableTable:
    Start Length Slot Name Signature
    	0 	31 		0 args [Ljava/lang/String;
    	8 	23 		1 lock Ljava/lang/Object;

从上面我们可以看出:synchronized 同步语句块的实现使用的是 monitorentermonitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。其中两个 monitorexit 指令是为了保证锁在同步代码块代码正常执行以及出现异常的这两种情况下都能被正确释放。

修饰方法时:

public synchronized void method() {
    System.out.println("synchronized 方法");
}

JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,如果是实例方法,JVM 会尝试获取实例对象的锁。如果是静态方法,JVM 会尝试获取当前 class 的锁。

总结

synchronized 同步语句块的实现使用的是 monitorentermonitorexit 指令,修饰方法是 ACC_SYNCHRONIZED 标识。

不过两者的本质都是对对象监视器 Monitor 的获取。
参考:https://github.com/Seazean/JavaNote/blob/main/Prog.md
https://blog.csdn.net/zhaocuit/article/details/100208879?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170239663116800182715111%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170239663116800182715111&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-100208879-null-null.142v96pc_search_result_base9&utm_term=java%E5%AF%B9%E8%B1%A1%E5%A4%B4&spm=1018.2226.3001.4187

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/249742.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于C/C++的rapidxml加载xml大文件 - 上部分翻译

RAPIDXML手册 版本 1.13 版权所有 &#xff08;C&#xff09; 2006&#xff0c; 2009 Marcin Kalicinski有关许可证信息&#xff0c;请参阅随附的文件许可证 .txt。 目录 1. 什么是 RapidXml&#xff1f; 1.1 依赖性和兼容性1.2 字符类型和编码1.3 错误处理1.4 内存分配1.5 …

纽扣电池是什么

纽扣电池 电工电气百科 文章目录 纽扣电池前言一、纽扣电池是什么二、纽扣电池的类别三、纽扣电池的作用原理总结前言 纽扣电池具有易于更换的特点,这使得它们成为许多便携设备的理想电源选择。但是,由于它们较小且外壳易于打开,所以家中有婴幼儿的家庭应特别注意将其放置在…

云原生之深入解析亿级流量架构之服务限流思路与方法

一、限流思路 ① 熔断 系统在设计之初就把熔断措施考虑进去&#xff0c;当系统出现问题时&#xff0c;如果短时间内无法修复&#xff0c;系统要自动做出判断&#xff0c;开启熔断开关&#xff0c;拒绝流量访问&#xff0c;避免大流量对后端的过载请求。系统也应该能够动态监测…

Pycharm第三方库导入失败避坑!

最近遇到了明明安装了 python 第三方库&#xff0c;但是在 pycharm 当中却导入不成功的问题。 使用Pycharm手动安装三方库和自动安装三方库都失败&#xff0c;以及Pycharm终端使用pip命令安装也未解决。网上找各种方法尝试都没成功&#xff0c;原来是一不小心就跳进了虚拟环境…

代码随想录算法训练营 | day52 动态规划 300.最长递增子序列,674.最长连续递增子序列,718.最长重复子数组

刷题 300.最长递增子序列 题目链接 | 文章讲解 | 视频讲解 题目&#xff1a;给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。…

彻底告别pip安装Python第三方库网速慢的问题

方式一、 如果使只下载没几个库 直接: pip install 库名 -i http://mirrors.aliyun.com/pypi/simple/ 方式二、 如果要一直下载库&#xff0c;就可以通过配置pip.ini文件来解决&#xff0c;具体步骤如下所示&#xff1a; 1、打开文件资源管理器 2、搜索%APPDATE% 3、创建一…

LeetCode 2415. 反转二叉树的奇数层:深度优先搜索(DFS)

【LetMeFly】2415.反转二叉树的奇数层&#xff1a;深度优先搜索(DFS) 力扣题目链接&#xff1a;https://leetcode.cn/problems/reverse-odd-levels-of-binary-tree/ 给你一棵 完美 二叉树的根节点 root &#xff0c;请你反转这棵树中每个 奇数 层的节点值。 例如&#xff0c…

CSS盒子的浮动与网页布局(重点,有电影页面案例)

浮动适用于那种盒子的并列布局 CSS 提供了三种传统布局方式(简单说,就是盒子如何进行排列顺序)&#xff1a;  普通流&#xff08;标准流&#xff09;  浮动  定位 标准流&#xff08;普通流/文档流&#xff09; 所谓的标准流: 就是标签按照规定好默认方式排列. 1. 块级…

win10电脑字体大小怎么设置?介绍四种方法

在Win10操作系统中&#xff0c;字体大小的设置对于用户来说是一个非常重要的问题。合适的字体大小能够保护我们的视力&#xff0c;提高我们的工作效率。本文将介绍几种常用的方法来调整Win10电脑的字体大小&#xff0c;帮助用户轻松设置自己喜欢的字体大小。 方法一&#xff1…

C语言写的 mini版的 http 服务器 , 很详细

文章目录 效果展示整体架构流程技术细节完整代码 效果展示 例如&#xff1a;htpp://192.168.23.140/home.html -> 正确的请求格式 home.html 这个资源是放在我们服务器里面的 , 并不是随便访问的资源,当然我们可以放很多的资源进去. 整体架构流程 整个实现的流…

源码解析:Apache RocketMQ重置消费位点

引入 reset offset&#xff0c;即重置消费进度&#xff0c;一般在以下场景中使用&#xff1a; 需要重新消费已经消费过的消息&#xff0c;重置到最早位置或根据时间进行重置。消息积压&#xff0c;不需要消费积压的消息&#xff0c;重置到最新位置&#xff0c;使其从最新位置…

订单管理系统开发经验的总结:优化流程、提升效率的关键实践

前言 一.订单管理系统的架构设计 二.订单系统的详细设计 1.拆分 2.换货 3.发货 4.拦截 5.取消 6.物流回传 三.订单系统的订单状态流转 初始状态 中间状态 异常状态 终态 四.订单系统的关键代码逻辑 五.结语 前言 两年来&#xff0c;整个订单管理系统经过大大小…

[MySQL]数据库概述

目录 1.什么是数据库 2.数据库分类 2.1关系型数据库 2.2非关系型数据库 1.什么是数据库 我们知道&#xff0c;存储数据可以使用文件来存储。那么为什么我们还要大费周章的去设计和使用数据库呢&#xff1f; 因为文件保存数据有以下几个缺点&#xff1a; 1.文件的安全性不…

Attention机制学习

写在前面 注意力机制是一个很不错的科研创新点方向&#xff0c;但是没有系统记录过学习过程&#xff0c;这里记录科研中遇到的各种注意力机制。 1. Attention机制解释 本质上来说用到attention的任务都有Query&#xff0c;Key&#xff0c;Value三个关键components&#xff0c;…

扩展学习|大数据挖掘与智能体ABM建模

出处&#xff1a;计算社会科学_中南大学_中国大学MOOC(慕课) (icourse163.org)https://www.icourse163.org/course/CSU-1466004186 ps&#xff1a;相关内容来自于本人笔记&#xff0c;若有需要请联系原作者&#xff01;个人学习留存&#xff0c;侵权必删&#xff01; 一…

可回收资源的环保螺旋盖葡萄酒

在酿酒师中&#xff0c;选择哪种瓶盖来保存一瓶葡萄酒主要取决于葡萄酒的种类和酿酒师自己的偏好。在20世纪70年代&#xff0c;澳洲朋友引进并推广了一种保存葡萄酒的新方法&#xff0c;这种新方法螺旋盖并在70年代获得专利&#xff0c;投入商业使用&#xff0c;澳大利亚的酿酒…

【STM32】STM32学习笔记-OLED显示屏(10)

00. 目录 文章目录 00. 目录01. OLED显示屏接线图02. OLED函数库03. OLED测试代码04. Keil调试05. 程序下载06. 附录 01. OLED显示屏接线图 02. OLED函数库 oled.h #ifndef __OLED_H #define __OLED_Hvoid OLED_Init(void); void OLED_Clear(void); void OLED_ShowChar(uint8…

图片变成动图如何操作?掌握这个办法就够了

生动有趣的gif动画图片是怎么制作的呢&#xff1f;其实&#xff0c;制作gif动图的方法很简单&#xff0c;无需下载任何软件&#xff0c;使用gif动图制作&#xff08;https://www.gif.cn/&#xff09;工具-GIF中文网。只需要上传jpg、png格式的图片&#xff0c;轻松一键就能在线…

Reinfocement Learning 学习笔记PartⅡ

文章目录 Reinfocement Learning六、随机近似与随机梯度下降&#xff08;Stochastic Approximation & Stochastic Gradient Descent&#xff09;6.1 Robbins-Monro Algorithm6.2 随机梯度下降 七、时序差分方法&#xff08;Temporal-Difference Learning&#xff09;7.1 TD…

OpenAI 承认 ChatGPT 最近的懒惰:由于用户体验到响应缓慢和无用的输出,调查正在进行中

文章目录 一. ChatGPT 指令遵循能力下降引发用户投诉1.1 用户抱怨回应速度慢、敷衍回答、拒绝回答和中断会话 二. OpenAI 官方确认 ChatGPT 存在问题&#xff0c;展开调查三. OpenAI 解释模型行为差异&#xff0c;回应用户质疑四. GPT-4 模型变更受人事动荡和延期影响 一. Chat…