【Java 并发编程】(二) 从对象内存布局开始聊 synchronized

对象的内存布局

首先抛出一个经典面试题: 一个 Object 对象占多大?
在这里插入图片描述

这里我用工具打印了出来, 发现是 “16bytes”, 也就是 16B; 为什么? 请继续往下看;

在这里插入图片描述

普通对象(除了数组), 由markword, 类型指针, 实例数据(就是对象里的成员), 对齐填充(整个对象大小要能被8B整数, 方便64bit总线)构成;

  • markword 中保存了监视器锁的信息, GC 的信息, 还可能保存默认的 hashCode 方法计算出的哈希值;

  • 类型指针指向这个对象所属的类的类对象;

  • java命令默认带两个压缩指针的参数, UseCompressedClassPointersUseCompressedOops(Ordinary Object Pointers), 在64位环境下, 一个指针应当占8字节; 第一个指令会将对象头中的类型指针压缩位4B, 第二个参数会将成员指针压缩为4B;

  • 一个java应用所占的内存大小, 几乎不会大到需要用 64 位的寻址空间, 32 位完全够了, 这是为什么可以开启指针压缩;

  • 对于Object类, markword 8B, 类型指针4B, Object类型没有实例数据0B, 对齐填充4B, 总共 16B

对象头

在这里插入图片描述

  • markword中记录了锁信息, GC信息(例如当前熬过了几次垃圾回收, 注意这个字段只有4bit最大值15)
  • 如果使用了默认的hashcode方法, 那么计算出来的 hash 值会被放到markword里保存; 而这个保存的地方, 和偏向锁是有冲突的, 如果调用过默认的 hashCode 方法, 偏向锁就不能用了;
  • 使用了偏向锁, 再调用默认的 hashCode, 会触发锁升级;

监视器锁

特点

  1. 对象级别的锁: 监视器锁是与对象关联的,每个对象都有一个内置的监视器锁。当一个线程获取了对象的监视器锁后,其他线程就无法同时获取该对象的监视器锁,直到锁被释放。

  2. 排他性访问: 监视器锁实现了排他性访问,即同一时刻只有一个线程能够持有对象的监视器锁,其他线程需要等待。

  3. 基于进入和退出监视器锁的规则: 线程可以进入同步代码块或同步方法,获取对象的监视器锁;当线程退出同步代码块或同步方法时,会释放对象的监视器锁。

  4. 可重入性: 监视器锁支持线程的可重入性,即同一个线程在持有锁的情况下可以重复地继续获取锁,而不会发生死锁。但是要注意获取多少次锁就需要释放多少次锁

  5. 等待和唤醒机制: 一个线程持有监视器锁时, 其它线程尝试获取锁时将被阻塞, 放到该锁的阻塞队列中; 持有锁的线程释放锁后, 如果有其他线程正在等待获取同一个对象的监视器锁,那么其中的一个线程会被唤醒;

Synchronized

synchronized代码块

synchronized(对象){...
}
  • 使用同步代码块的线程执行时如果出现异常, 会自动释放锁;
  • 小括号内的对象又称互斥条件, 是同步代码块锁住的对象; 可以使用this, 某个特定类的对象, 某个类的类对象;
  • 当线程进入 synchronized 代码块时,它会尝试获取对象的监视器锁。如果对象的监视器锁已经被其他线程持有,则当前线程会被阻塞,直到获取到锁为止。获取到锁后,线程就可以执行 synchronized 代码块中的代码,直到代码块执行完毕或者抛出异常,然后释放锁。
  • 具有可重入的特点;

synchronized方法

  • synchronized关键字加在静态方法上, 则进入方法时会尝试获取该静态方法所属的类的类对象的监视器锁;
  • synchronized加在成员方法上, 进入方法时会尝试获取调用该方法的对象的的监视器锁;
  • 退出方法时释放锁;

synchronized实现原理

  1. 源码中, synchronized关键字
  2. 编译后变为对监视器锁的操作, monitor enter 和 monitor exit, 实际就是对对象头中markword的修改
  3. 在JVM执行过程中, 会自动进行锁升级
  4. 最终依靠汇编指令, lock_cmpxchg指令实现

非公平

  1. synchronized是非公平锁, 当一个线程尝试获取锁时, 不会优先让阻塞队列中的线程获取锁; 而是一上来自己就尝试获取锁;
  2. 通常是唤醒队头元素, 通常也就是等待时间最久的元素

wait / notify

  • 需要在synchronized代码块内部或者synchronized方法内部, 已经获取了某个对象的监视器锁的前提下, 才能用这个对象去调用wait/notify方法

  • wait:

    • 调用之前必须先获取到该对象的监视器锁
    • 某个对象调用wait方法后, 当前线程会释放这个对象的监视器锁, 并进入 WAITING 状态, 加入到该监视器锁的waiting队列中, 等待其他线程调用相同对象的notify/ notifyAll方法唤醒;
  • notify:

    • 调用之前必须先获取到该对象的监视器锁
    • 调用notify方法后会在该监视器锁的waiting队列中唤醒一个线程;
    • 被唤醒的线程会重新尝试获取该对象的监视器锁; 获取到以后才能继续执行; 获取失败就blocked
  • notifyAll:

    • 与notify的区别是会唤醒监视器锁waiting队列中的所有线程

为什么要在获取监视器锁以后才能 wait 和 notify ? 主要是为了防止一次 notify 调用导致多个 WAITING 状态的线程被唤醒; 在 synchronized 代码块内部, 唤醒时需要重新获取监视器锁, 避免多个线程能同时往下执行;

锁升级

  • synchronized 获取对象的锁时, 获取方式会根据线程竞争情况进行升级; 因为重量级锁是要向操作系统申请的, 时间消耗比较大;

  • 在Java中,锁的状态包括偏向锁、轻量级锁和重量级锁。

  • 偏向锁: 当一个线程第一次进入同步块时,会尝试获取对象的偏向锁, 本质就是通过 CAS(下文会有详细介绍) 的方式, 将指向本线程的指针, 写到对象头里; 以后相同线程再尝试获取相同对象的监视器锁时, 会直接获取成功;

  • 轻量级锁: 当有不同线程尝试获取锁时, 锁状态升级为轻量级锁, 轻量级锁使用CAS操作来尝试获取锁。和获取偏向锁类似, 用CAS操作尝试将自己的 LockRecord 指针写入对象头来实现获取锁; 每个线程有自己的方法栈, 方法栈里有LockRecord; 如果获取失败,线程就会进行自旋操作继续尝试, 自旋达到一定次数后(由JVM动态计算此阈值),轻量级锁会升级为重量级锁;

    为什么变成用 LockRecord了? 接着用线程指针不行吗? 应该需要保存一些额外的信息了; 比如说自旋次数;

  • 重量级锁: 将获取锁失败的线程阻塞;

    重量级锁是向操作系统申请获取的, 重量级锁的操作都是由操作系统负责的;

    当某个线程获取重量级锁失败时, 会进入该锁对应的阻塞队列中;

    当临界区大, 并发级别高时, 适合使用重量级锁, 避免cpu资源的消耗;临界区小, 并发级别低时, 适合使用轻量级锁

  • 补充两个概念, 知道就行

    锁粗化

    JVM检测到一连串的操作都是对同一个对象进行加锁的话, 会将锁的范围粗化到一系列操作的外部, 只进行一次加锁;

    StringBuffer是线程安全的, append方法需要加锁;

    while(i < 100){
    i++;
    sb.append("hello");
    }
    

    锁消除

    当确定某个锁不必要的时候, 例如一个方法中有StringBuffer类型的局部变量, 其它线程不可能访问到这个局部变量, 那么会自动消除StringBuffer内的加锁操作

在Java中,监视器锁(也称为内置锁或对象锁)在实现上通常包含两种队列:

  1. 等待队列(Wait Set):等待队列用于存放那些因为调用了 Object.wait()Thread.join() 或者 LockSupport.park() 等方法而进入等待状态的线程。当线程被唤醒时,它会从等待队列中移出,重新进入锁的竞争中。
  2. 阻塞队列(Blocked Set):阻塞队列用于存放那些尝试获取锁但由于锁被其他线程持有而进入阻塞状态的线程。这些线程会被放入锁的阻塞队列中等待锁的释放。

CAS

  • CAS(Compare and Swap)是一种原子性操作, 能够在无锁的情况下实现安全的值更新操作;

  • 也可以宽泛一点就说成是自旋

  • 它的基本思想是,要修改某个值时, 先读取当前值, 记为e, 计算出要修改为的值v, 写回之前再次读取当前值n, 如果n和e不相同, 说明修改失败, 放弃修改, 重新执行此过程

  • 多核环境下, 底层是由 lock_cmpxchg 汇编指令实现的, cmpxchg这条指令并没有原子性, 必须是lock_cmpxchg指令, 前面的lock指令保证了原子性;

  • 例如由CAS + 自旋机制 实现轻量级锁机制

    1. 线程尝试获取锁时,首先会通过CAS操作尝试获取轻量级锁
    2. 如果获取锁失败, 通过自旋操作不断尝试获取锁
    while(!cas){
    count++;
    if(cnt > threshold)
    升级重量级锁;
    }
    
  • ABA问题

    当我写回之前再次读取当前值n, 即使仍然等于e, 我也不知道到底是没被人改过, 还是改过只不过最后还是改成了e;

    解决: 给值添加版本号, 读取值的时候连同版本号一起读取, 如果值相同但是版本号不同, 说明已经被修改过;

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

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

相关文章

电销机器人引领电销变革

以前电销都是都是通过盲打&#xff0c;现在有了电话机器人的出现&#xff0c;为电销公司带来新的篇章。 我们都知道外呼中心的人员离职率始终居高不下&#xff0c;人员的培训频繁成本很高&#xff0c;外部电话水平参差不齐&#xff0c;服务态度不够稳定等问题&#xff0c;都是难…

基础 - 前端知识体系详解

一、前端三要素 HTML&#xff08;结构&#xff09;&#xff1a; 超文本标记语言&#xff08;Hyper Text Markup Language&#xff09;&#xff0c;决定网页的结构和内容。CSS&#xff08;表现&#xff09;&#xff1a; 层叠样式表&#xff08;Cascading Style Sheets&#xff0…

【Mac】Downie 打开提示试用的解决办法?

前情 我们在使用 Downie 的时候&#xff0c;可能遇到提示试用的问题&#xff0c;如下图所示。 原因 旧版本的 Downie 没有卸载干净导致的。 解决办法 先使用 AppCleaner 卸载掉电脑上的 Downie 旧版本软件&#xff0c;必须使用 AppCleaner 卸载。重新安装 Downie 即可。

如何保证数据不丢失?(死信队列)

死信队列 1、什么是死信 死信通常是消息在特定的场景下表现&#xff1a; 消息被拒绝访问消费者发生异常&#xff0c;超过重试次数消息的Expiration过期时长或者队列TTL过期时间消息队列到达最大容量 maxLength 2、什么是死信队列 只由死信构成的消息队列是死信队列 死信队…

PhpStorm完全配置指南:打造高效PHP开发环境!

Phpstorm环境配置与应用&#xff0c;具体包括安装PhpStorm、配置PHP运行环境、Apache集成、调试和部署等步骤。下面将详细展开每个步骤的具体操作和注意事项。 PhpStorm的下载与安装 下载地址&#xff1a;访问PhpStorm的官网下载地址&#xff0c;选择合适的版本进行下载。建议选…

springboot、spring@JsonAlias(velue)的作用

‌JsonAlias注解用于为字段或参数指定反序列化时的别名。‌‌ 在Java开发中&#xff0c;‌特别是在使用Jackson库进行JSON序列化和反序列化时&#xff0c;‌JsonAlias注解扮演着重要的角色。‌它允许开发者为字段或参数指定一个或多个别名&#xff0c;‌这样在反序列化过程中&…

希尔排序,详细解析(附图解)

1.希尔排序思路 希尔排序是一种基于插入排序的算法&#xff0c;通过将原始数据分成若干个子序列&#xff0c;然后对子序列进行插入排序&#xff0c;逐渐减小子序列的间隔&#xff0c;最后对整个序列进行一次插入排序。 1.分组直接插入排序&#xff0c;目标接近有序--------…

玩机进阶教程-----回读 备份 导出分区来制作线刷包 回读分区的写入与否 修改xml脚本

很多工作室需要将修改好的系统导出来制作线刷包。前面分享过很多制作线刷包类的教程。那么一个机型中有很多分区。那些分区回读后要写入。那些分区不需要写入。强写有可能会导致不开机 不进系统的故障。首先要明白。就算机型全分区导出后在写回去 都不一定可以开机进系统。那么…

萌啦数据插件使用情况分析,萌啦数据插件下载

在当今数字化时代&#xff0c;数据已成为企业决策与个人分析不可或缺的重要资源。随着数据分析工具的日益丰富&#xff0c;一款高效、易用的数据插件成为了众多用户的心头好。其中&#xff0c;“萌啦数据插件”凭借其独特的优势&#xff0c;在众多竞品中脱颖而出&#xff0c;成…

基于SpringBoot的房屋交易平台的设计与实现

TOC springboot235基于SpringBoot的房屋交易平台的设计与实现 第1章 绪论 1.1 研究背景 互联网概念的产生到如今的蓬勃发展&#xff0c;用了短短的几十年时间就风靡全球&#xff0c;使得全球各个行业都进行了互联网的改造升级&#xff0c;标志着互联网浪潮的来临。在这个新…

基于Web的农产品直卖平台的设计与实现

TOC springboot266基于Web的农产品直卖平台的设计与实现 绪论 1.1 选题背景 当人们发现随着生产规模的不断扩大&#xff0c;人为计算方面才是一个巨大的短板&#xff0c;所以发明了各种计算设备&#xff0c;从结绳记事&#xff0c;到算筹&#xff0c;以及算盘&#xff0c;到…

电路板元器件识别指南:电子技术核心知识

电子设备无处不在&#xff0c;从我们日常使用的手机到复杂的医疗设备&#xff0c;背后都有一个共同的组件——电路板。电路板上的各种元器件是电子设备功能实现的基础。在此&#xff0c;道合顺帮助读者们理解电路板上不同元器件的作用与识别方法&#xff0c;从而更好地维护和使…

Spring boot logback日志框架加载初始化源码

##LoggingApplicationListener监听 Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationStartingEvent) {onApplicationStartingEvent((ApplicationStartingEvent) event);}else if (event instanceof ApplicationEnvironment…

2024最新金三银四必问面试题大全

我花了三天时间&#xff0c;整理了100道最经典的常见测试面试题&#xff08;附答案&#xff09;&#xff01;完整版文档见文末&#xff01; 1、所做项目的情况&#xff0c;主要做什么类型的测试&#xff1f; 2、你在测试中发现了一个bug&#xff0c;但是开发经理认为这不是一个…

安卓主板_MTK联发科主板定制开发|PCBA定制开发

MTK联发科安卓主板&#xff0c;采用MT6762八核平台方案&#xff0c;支持谷歌Android 11.0系统&#xff0c;MT6762采用ARM八核A53内核芯片、主频高达2.0GHz&#xff0c;GPU采用ARM PowerVR GE8329650MHZ&#xff0c;支持主流19201080分辨率&#xff0c;支持硬解H.264&#xff0c…

【TabBar嵌套Navigation案例-设置页面-解析plist Objective-C语言】

一、我们来做这个设置页面 1.先看一下我们的示例程序,当我们点击上边的这个齿轮时候, 会跳到一个Controller里边来,然后呢,这个Controller,是一个TableView,组的样式, 我们先把这个小功能,先做了,再来说设置页面里边的东西啊, 首先呢,点击这个齿轮的时候,我要找到…

常用任务管理函数

任务挂起函数 void vTaskSuspend( TaskHandle_t xTaskToSuspend ) 有比较简单的两点需要注意。 第一&#xff0c;使用任务挂起函数必须将INCLUDE_vTaskSuspend配置为1。 第二&#xff0c;实参为要挂起任务的句柄。 void vTaskSuspendAll&#xff08;void&#xff09; 这个…

仓库与文件的提交/修改/删除

目录 1. 创建仓库 ​编辑​编辑 2. 界面解释 3. 再创建仓库 4. 移除仓库 5. 本地添加文件到仓库 ​编辑​编辑 6. 比对&#xff0c;提交 7. 修改&#xff0c;提交 8. 查询版本号 9. 删除文件 git 是分布式版本控制工具&#xff0c;需要将中央服务器克隆到本地&#…

Android逆向题解-攻防世界app2

又是一个花里胡哨的题。 一通分析没啥用。 最后指令启动界面就ok。 adb shell am start -n com.tencent.testvuln/com.tencent.testvuln.FileDataActivity 还是看一眼解密代码 protected void onCreate(Bundle bundle0) {super.onCreate(bundle0);this.setContentView(0x7F030…

东南亚TikTok直播:直播专线的重要性及其优势

在东南亚地区进行TIKTOK直播时&#xff0c;是否需要使用专线呢&#xff1f;答案是肯定的。因为你的客户群体面向东南亚&#xff0c;需要东南亚IP直播地址&#xff0c;才能够推送精准的东南亚用户到你的直播间。本文将讨论使用TikTok直播专线的一些优势。 使用专线直播可以确保获…