synchronized关键字以及底层实现

目录

基本使用

底层实现

synchronized锁升级

对象的内存结构

ⅰ. 对象头

1. ① 运行时元数据 (Mark Word) (占64位)

a. 哈希值 (HashCode)

b. GC分代年龄

c. 锁状态标记

2. ② 类型指针: (Klass Point) (占 32位)

ⅱ. 实例数据

ⅲ. 对齐填充

Moniter重量级锁

轻量级锁

偏向锁


基本使用

Java中的synchronized关键字主要用于实现线程同步,确保在多线程环境下同一时间只有一个线程可以访问被它保护的代码块或方法。以下是synchronized的基本使用方式:

1.修饰非静态方法

public class MyClass {private int count;// 一个同步实例方法public synchronized void increment() {count++;}
}

 在这个例子中,synchronized关键字修饰了increment()方法。这意味着任何时刻只有一个线程可以执行该方法,其他试图同时访问此方法的线程将会阻塞,直到当前线程完成方法调用并释放锁。

 2.修饰静态方法

public class MyClass {private static int staticCount;// 一个同步静态方法public static synchronized void incrementStatic() {staticCount++;}
}

synchronized修饰静态方法时,它锁定的是类对象(即Class级别的锁),因此,在多线程环境中,所有对该类静态方法的访问将被串行化。 

 3.修饰代码块

public class MyClass {private final Object lock = new Object();private int instanceCount;public void incrementInstanceCount() {// 同步代码块synchronized (lock) {instanceCount++;}}
}

这里synchronized用于一个代码块,它可以根据需要指定一个特定的对象作为锁对象(这里是lock对象)。只有获取到这个对象锁的线程才能进入并执行同步代码块内的内容。

底层实现

 javap -v xx.class   查看class字节码信息

synchronized关键字在Java中的底层原理主要涉及操作系统层面的互斥锁(Monitor)机制,以及JVM内部的实现细节

JVM使用monitorenter和monitorexit指令来实现对监视器的操作。

  • 当执行到monitorenter指令时,如果对象没有被锁定或者当前线程已经持有该对象的锁,则将锁计数器加1,并允许线程继续执行同步代码块。
  • 当执行到monitorexit指令时,锁计数器减1;当计数器为0时,表示锁被释放,其他等待该锁的线程可以有机会竞争获取锁并执行相应代码。

Monitor 被翻译为监视器,是由jvm提供,c++语言实现

Owner:存储当前获取锁的线程的,只能有一个线程可以获取

EntryList:关联没有抢到锁的线程,处于Blocked状态的线程

WaitSet:关联调用了wait方法的线程,处于Waiting状态的线程

synchronized锁升级

Monitor实现的锁属于重量级锁,里面涉及到了用户态和内核态的切换、进程的上下文切换,成本较高,性能比较低。

synchronized锁升级是Java 1.6及以后版本引入的一种优化机制,目的是为了在多线程环境下更好地平衡锁的性能和资源消耗。在早期版本中,Java对于synchronized关键字的实现是直接使用重量级锁(即操作系统互斥量Mutex),这种方式虽然能够确保线程安全,但其开销较大,尤其是在锁竞争不激烈的情况下。

在HotSpot虚拟机中,对象在内存中存储的布局可分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充

对象的内存结构

ⅰ. 对象头

1. ① 运行时元数据 (Mark Word) (占64位)
a. 哈希值 (HashCode)

31位的对象标识,采用延迟加载技术,调用 System.identityHashCode() 计算得到,并将结果写到对象头中,用于栈空间中,对对象的引用的指向,不然是无法找到堆中的对象的

b. GC分代年龄

(占用4位) 记录着幸存者区对象被GC过后的age, 一般age为15, 如果对象的gc次数到达了这个值,将进入老年代

c. 锁状态标记

记录加锁的一些信息

  1. hashcode:25位的对象标识Hash码
  2. age:对象分代年龄占4位
  3. biased_lock:偏向锁标识,占1位 ,0表示没有开始偏向锁,1表示开启了偏向锁
  4. thread:持有偏向锁的线程ID,占23位
  5. epoch:偏向时间戳,占2位
  6. ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针,占30位
  7. ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针,占30位
2. ② 类型指针: (Klass Point) (占 32位)

是对方法区中,类的元信息的引用

ⅱ. 实例数据

真实的记录一个对象包含的数据,比如Cat类对象 ,里面包含 name,age 等等相关的信息,如果有字符串,要引用字符串常量池

ⅲ. 对齐填充

仅起到占位符的作用 ,因为 HotSpot虚拟机要求对象的起始地址必须是8字节的整数,如果达不到,用对齐添充的方式补齐,为什么是8呢,因为64位的机器能被8整除,效率高

Moniter重量级锁

每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针

轻量级锁

在很多的情况下,在Java程序运行时,同步块中的代码都是不存在竞争的,不同的线程交替的执行同步块中的代码。这种情况下,用重量级锁是没必要的。因此JVM引入了轻量级锁的概念。

加锁流程

1.在线程栈中创建一个Lock Record,将其obj字段指向锁对象。
2.通过CAS指令将Lock Record的地址存储在对象头的mark word中,如果对象处于无锁状态则修改成功,代表该线程获得了轻量级锁。
3.如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一部分为null,起到了一个重入计数器的作用。
4.如果CAS修改失败,说明发生了竞争,需要膨胀为重量级锁。

解锁过程

1.遍历线程栈,找到所有obj字段等于当前锁对象的Lock Record。
2.如果Lock Record的Mark Word为null,代表这是一次重入,将obj设置为null后continue。
3.如果Lock Record的 Mark Word不为null,则利用CAS指令将对象头的mark word恢复成为无锁状态。如果失败则膨胀为重量级锁。

偏向锁

轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。 Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现 这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux第46步_通过“添加自定义菜单”来学习menuconfig图形化配置原理

通过“添加自定义菜单”来学习menuconfig图形化配置原理,将来移植linux要用到。 自定义菜单要求如下: ①、在主界面中添加一个名为“My test menu”,此菜单内部有一个配置项。 ②、配置项为“MY TESTCONFIG”,此配置项处于菜单“My test m…

Python 函数式编程进阶:map、filter、reduce

Python 函数式编程进阶:map、filter、reduce 介绍map 函数作用和语法使用 map 函数Lambda 函数的配合应用 filter 函数作用和语法使用 filter 函数Lambda 函数的结合运用 reduce 函数作用和语法使用 reduce 函数典型应用场景 介绍 在函数式编程中,map、…

联想thinkpad-E450双系统升级记

早期笔记本联想thinkpad-E450双系统 大约16年花4000多大洋,买了一台thinkpad-E450屏幕是16寸本,有AMD独立显卡,i5cpu,4G内存。 . 后来加了一个同型号4G内存组成双通道, . 加了一个三星固态500G, . 换了一个…

2.10日学习打卡----初学RocketMQ(一)

2.10日学习打卡 对于MQ(Message queue)消息队列的一些解释可以看我原来写的文章 初学RabbitMQ 各大MQ产品比较 一.RocketMQ概述 发展历程 RocketMQ概念术语 生产者和消费者 生产者负责生产消息,一般由业务系统负责生产消息,消费者即后台系统&…

图灵日记--MapSet字符串常量池反射枚举Lambda表达式泛型

目录 搜索树概念实现性能分析和 java 类集的关系 搜索概念及场景模型 Map的使用Map常用方法 Set的说明常见方法说明 哈希表冲突-避免-负载因子调节冲突-解决-闭散列冲突-解决-开散列/哈希桶冲突严重时的解决办法 实现和 java 类集的关系 字符串常量池String对象创建intern方法 …

C#,最大公共子序列(LCS,Longest Common Subsequences)的算法与源代码

1 最大公共子序列 最长的常见子序列问题是寻找两个给定字符串中存在的最长序列。 最大公共子序列算法&#xff0c;常用于犯罪鉴定、亲子鉴定等等的 DNA 比对。 1.1 子序列 让我们考虑一个序列S<s1&#xff0c;s2&#xff0c;s3&#xff0c;s4&#xff0c;…&#xff0c;…

Netty应用(五) 之 Netty引入 EventLoop

目录 第三章 Netty 1.什么是Netty&#xff1f; 2.为什么需要使用Netty&#xff1f; 3.Netty的发展历程 4.谁在使用Netty&#xff1f; 5.为什么上述这些分布式产品都使用Netty&#xff1f; 6.第一个Netty应用 7.如何理解Netty是NIO的封装 8.logback日志使用的加强 9.Ev…

【网站项目】028蜀都天香酒楼管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

腾讯云4核8G服务器可以用来干嘛?怎么收费?

腾讯云4核8G服务器适合做什么&#xff1f;搭建网站博客、企业官网、小程序、小游戏后端服务器、电商应用、云盘和图床等均可以&#xff0c;腾讯云4核8G服务器可以选择轻量应用服务器4核8G12M或云服务器CVM&#xff0c;轻量服务器和标准型CVM服务器性能是差不多的&#xff0c;轻…

【数据结构】哈希表的开散列和闭散列模拟实现

哈希思想 在顺序和树状结构中&#xff0c;元素的存储与其存储位置之间是没有对应关系&#xff0c;因此在查找一个元素时&#xff0c;必须要经过多次的比较。 顺序查找的时间复杂度为0(N)&#xff0c;树的查找时间复杂度为log(N)。 我们最希望的搜索方式&#xff1a;通过元素…

15 ABC基于状态机的按键消抖原理与状态转移图

1. 基于状态机的按键消抖 1.1 什么是按键&#xff1f; 从按键结构图10-1可知&#xff0c;按键按下时&#xff0c;接点&#xff08;端子&#xff09;与导线接通&#xff0c;松开时&#xff0c;由于弹簧的反作用力&#xff0c;接点&#xff08;端子&#xff09;与导线断开。 从…

【教3妹学编程-算法题】执行操作后的最大分割数量

2哥 : 3妹&#xff0c;今年过年收到压岁钱了没呢。 3妹&#xff1a;切&#xff0c;我都多大了啊&#xff0c;肯定没收了啊 2哥 : 俺也一样&#xff0c;不仅没收到&#xff0c;小侄子小外甥都得给&#xff0c;还倒贴好几千 3妹&#xff1a;哈哈哈哈&#xff0c;2叔叔&#xff0c…