Synchronize和Volatile底层实现原理

synchronized和volatile关键字实现和底层原理详解 需要有一个地方来存储抢占锁的标记,否则当其他线程来抢占资源时,不知道当前是应该正常执行还是应该排队。实际上,这个锁标记是存储在对象头中的。 阅读详情

最近在看并发编程的艺术这本书,希望加深对并发这块的理解。毕竟并发相关还是十分重要的。本文主要是关于第二章Java并发机制的底层实现原理的相关记录。主要包括volatile,synchronized,原子操作等实现原理的分析。

更多文章欢迎访问我的个人博客–>幻境云图

1. 上下文切换

多线程

即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。

什么是上下文切换

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。

所以任务从保存到再加载的过程就是一次上下文切换。

上下文切换也会影响多线程的执行速度

因为线程有创建和上下文切换的开销,所以有时候并发不一定比串行快。

减少上下文切换的办法

减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。

  • 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一
    些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
  • CAS算法。Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
  • 使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这
    样会造成大量线程都处于等待状态。
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

2. volatile关键字

如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。

有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,其中就包括了Lock前缀.Lock前缀的指令在多核处理器下会引发了两件事情。

1)将当前处理器缓存行的数据写回到系统内存。

Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号。在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以独占任何共享内存。

如果访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反,它会锁定这块内存区
域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁
定”,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。

2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。如果通过嗅探一个处理器来检测其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充。

3. synchronized原理与应用

Java SE 1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。

Java中的每一个对象都可以作为锁。具体表现
为以下3种形式。

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。
    当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

3.1 底层实现

JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。

代码块同步是使用monitorenter和monitorexit指令实现的.

而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用这两个指令来实现。

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

3.2 Java对象头

synchronized用的锁是存在Java对象头里的。

java的对象头由以下三部分组成:

1,Mark Word

2,指向类的指针

3,数组长度(只有数组对象才有)

3.3 锁的升级与对比

Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状
态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。

偏向锁

HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。

当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

偏向锁解除

偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有正在执行的字节码)。它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态;如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程.

轻量级锁

(1)轻量级锁加锁
线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
(2)轻量级锁解锁
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

优缺点比较

4. 原子操作的实现原理

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

4.1 处理器如何实现原子操作

处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。

第一个机制是通过总线锁保证原子性。

所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。

第二个机制是通过缓存锁定来保证原子性。

总线锁定的开销比较大,目前处理器在某些场合下使用缓存锁定代替总线锁定来进行优化。

所谓“缓存锁定”是指内存区域如果被缓存在处理器的缓存行中,并且在Lock操作期间被锁定,那么当它执行锁操作回写到内存时,处理器不在总线上声言LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效.

4.2 Java如何实现原子操作

使用循环CAS实现原子操作

JVM中的CAS操作正是利用了处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。

CAS实现原子操作的三大问题

1.ABA问题

但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1。

2.循环时间长开销大

自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。

3.只能保证一个共享变量的原子操作

操作多个共享变量时无法使用CAS操作,这个时候就可以用锁。还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。

使用锁机制实现原子操作

锁机制保证了只有获得锁的线程才能够操作锁定的内存区域。除了偏向锁,JVM实现锁的方式都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时候使用循环CAS释放锁。

参考

本文内容来自Java并发编程的艺术

A robust and easy to implement method for IMU calibration without external equipments(IMU标定) A robust and easy to implement method for IMU calibration without external equipments 1 标定原理1 亲试可用,还有动态演示效果不错。相比商用产品的简易标定流程,该标定方法有以下几点不同: 给出了加速度计和陀螺仪更复杂的模型。 利用Allan方差的定义,标定陀螺仪的偏置。 大量冗余数据,保证最优化收敛。利用数十... 阅读详情

相关推荐

人形机器人核心架构梳理

定义:机器人是能进行运动、操纵或定位且具有一定程度自主能力的可编程执行机构。按外在形态分类可分为传统机器人和人形机器人,其中人形机器人是一种利用人工智能和机器人技术制造的具有类似人类外观和行为的机器人。

小海聊智造 1万+

synchronize底层实现原理以及相关的优化

首先来说下synchronize和Lock的区别: 两者都是锁,用来控制并发冲突,区别在于Lock是个接口,提供的功能更加丰富,除了这个外,他们还有如下区别: synchronize自动释放锁,而Lock必须手动释放,并且代码中出现异常会导致unlock代码不执行,所以Lock一般在Finally中释放,而synchronize释放锁是由JVM自动执行的。 Lock有共享锁的概念,所以可以设...

淡定一生2333的博客 3万+

从JTAG到AS模式:一文搞懂EP4CE10E22C8N所有配置引脚(MSEL, nCONFIG, DATA0)的用法

本文深入解析EP4CE10E22C8N FPGA的配置引脚(MSEL, nCONFIG, DATA0)在不同模式下的使用方法,特别关注从JTAG到AS模式的切换要点。通过详细的配置模式对比、状态机分析和PCB布局建议,帮助开发者避免常见陷阱,提升FPGA配置成功率。

weixin_33713350的博客 319

Synchronized的底层实现原理(原理解析,面试必备)

synchronize的底层实现原理和monitor的实现机制

weixin_42460087的博客 3万+

【MATLAB+VScode】VScode调试MATLAB代码的终极指南

本文详细介绍了在VScode中调试MATLAB代码的完整方案。通过配置MATLAB Engine for Python作为桥梁,实现在VScode中设置断点、单步执行、查看变量等交互式调试功能,有效结合了VScode的现代化编辑体验与MATLAB的强大计算内核,显著提升开发效率。

weixin_29161719的博客 412

从入门到精通:dirmap 高级 Web 目录扫描实战指南

本文提供了一份从环境搭建到高级配置的dirmap Web目录扫描实战指南。详细解析了工具安装、核心配置文件dirmap.conf的深度调优,包括递归扫描、四种扫描模式(字典、爆破、爬虫、Fuzz)的实战应用,并演示了分阶段资产发现的完整工作流程,帮助安全人员高效发现隐藏目录与敏感文件。

rnn9storyteller的博客 481

线程安全问题

问题 线程安全是多线程领域的问题,线程安全可以简单理解为一个方法或者一个实例可以在多线程环境中使用而不会出现问题 在 Java 多线程编程当中,提供了多种实现 Java 线程安全的方式: 1)最简单的方式,使用 Synchronization 关键字 2)使用 java.util.concurrent.atomic 包中的原子类,例如 AtomicInteger 3)使用 java.util.concurrent.locks 包中的锁 4)使用线程安全的集合 ConcurrentHashMap 5)使用 v

weixin_32128491的博客 3895

模型实战(16)之StrongSort (OSNET)配合YOLOv5、v7、v8 实现多目标跟踪详解

StrongSort (OSNET)配合YOLOv5、v7、v8 实现多目标跟踪详解

yohnyang的博客 1770

Java synchronized关键字的底层实现以及锁升级优化的原理【一万字】

介绍了synchronized关键字实现锁的底层原理以及JDK对于synchronized做出的锁升级优化!

终生学习践行者 4845

QGis二次开发 —— 5.1、基于qgis开发雷达扫描、无人机飞行跟踪等(附:源码及工程)

基于qgis开发雷达扫描、无人机飞行跟踪等

324

基于DPCNN实现电商情感分析任务

下载链接项目中使用的模型是DPCNN,在模型中我们定义了三个组件,分别是embedding层,卷积层和全连接层。Embedding层:将每个词生成对应的嵌入向量,就是利用一个连续型向量来表示每个词卷积层:提取语句中的语义信息Linear层:将结果映射成2大小用于二分类,即正反面的概率。

优快云 精品推荐 1629

一文详解synchronized底层实现原理

synchronized底层实现基础知识并发编程的三个问题可见性(1)什么是可见性?(2)实例演示(3)保证可见性方式一:使用`synchronized`加锁方式二:`volatile`修饰共享变量原子性(1)什么是原子性?(2)实例演示(3)保证原子性有序性(1)什么是有序性(2)实例演示(3)`as-if-serial`语义(4)保证有序性`Java`内存模型:`JMM(JavaMemoryModel)`(1)什么是`JMM`?(2)具体实现`计算机的内存模型`:现在开始聊一下`JMM`:规定关系作用.

多回头看一看 4182

科研问答:医药类本科生(临床、中医、中药与护理)本科阶段是否需要开展科研学习?

医药类本科生(临床、中医、中药与护理)本科阶段是否需要开展科研学习?

MD Code 1507

Java 并发机制底层实现 —— volatile 原理、synchronize 锁优化机制、原子操作

概述 相信大家都很熟悉如何使用 Java 编写处理并发的代码,也知道 Java 代码在编译后变成 Class 字节码,字节码被类加载器加载到 JVM 里,JVM 执行字节码,最终需要转化为汇编指令在 CPU 上执行。因此,Java 中所使用的并发机制其实是依赖于 JVM 的实现和 CPU 的指令,所以了解 Java 并发机制的底层实现原理也是很有必要的 volatile 的应用 volatile 在多处理器开发中保证了共享变量的可见性。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能立即读取到修改

qq_38991247的博客 266

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

并发编程——Volatile原理

摘要 主要是分析Volatile的这个关键字和涉及到一些原理。 测试案例 JMM(JavaMemoryModel) JMM:Java内存模型,是java虚拟机规范中所定义的一种内存模型,Java内存模型是标准化的,屏蔽掉了底层不同计算机的区别(注意这个跟JVM完全不是一个东西,只有还有小伙伴搞错的)。 其实早期计算机中cpu和内存的速度是差不多的,但在现代计算机中,cpu的指令速度远超内存的存取速度,由于计算机的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读

庄小焱 743

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Java并发

1、 volatile 关键字 volatile是一个类型修饰符的指令关键字。 程序运行时,各个线程会将变量从主存拷贝到工作内存中,这就导致了工作内存中修改的变量值并不能实时和 主存中的值保持一致,当多线程同时操作时就会出现脏读现象。volatile避免了这一现象。在对volatile修饰的变量进行写操作是,会添加lock指令,当前运行内存修改后会实时写回系统内存。synchr...

ZXDstart的专栏 180

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

《Java并发编程的艺术》笔记 ------ java并发机制底层实现原理

java实现并发方式 jdk的关键字: volatile和synchronize 原子操作 volatile定义和实现原理 定义:java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致性更新,线程应该确保通过排它锁单独获取这个变量。(java中一个变量被声明称volatile,java线程内存模型确保所有线程看到变量变化后的值 并且保持一致) volatile实现可见性方式:...

qq_42725307的博客 232

高并发,线程,锁

并发编程的挑战 上下文切换 即使是单核处理器也是支持多线程的,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的执行时间,因为时间片非常短,所以CPU通过不停的切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒。 CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是在切换前会保存你上一个任务的状态,以便下次切换回这个任务时,可以在加载这个任务的状态。所以任务从保存到在加载的过程就是一次上下文切换。 类似读书,一本英文原著和一

weixin_44075325的博客 483

多线程面试题

synchronize 底层原理 每个对象都有一个监视器锁(monitor),通过monitorEnter和monitorExist尝试获取锁和释放锁,当monitor的进入数为0时,通过monitorEnter指令尝试将进入数设置为1表示获取到锁,synchronize是可重入锁(double check的单例模式是最好证明)。 Lock基本思想 个volatile类型变量用来表示锁的状态 一...

weixin_40882266的博客 223

Java高并发那点事儿

从事Java编程这么八年对,从事视频广告也有五年,感觉积累了很多技术和广告行业经验,然后当表达的时候却发现表达不出来,终其原因是总结的太少,写的太少,对知识而言一直是看多,写少。写也更多是在实战用法方面,没有更多的总结。接下来我讲对这么多年掌握的技术、广告行业经验进行一个总体的总结思考,广告引擎是一个高并发、高可用的系统

技术人宝哥 936

多线程的一些知识点

并发/线程的一些概念,概念很重要,没有理论指导的话会导致理解不够全面多线程问题的本质 -- 有序性,可见性,原子性synchronize的底层实现volatile的底层实现无锁算法的底层实现 -- CAS原子变量的实现final关键字的新含义 深入理解Java内存模型指令重排序和内存屏障顺序一致性和Happens-before规则一些基本的硬件知识一些处理同步问题的技术

pifengyz的专栏 274

Java并发编程的艺术学习笔记(二)

Java并发机制的底层实现原理(volatile) 1. volatile 1.1 volatile的作用 volatile是轻量级的synchronize,它在多处理器 开发中保证了共享变量的“可见性”。所谓可见性是指当一个线程修改共享变量时,其它线程能立即读取到该已被修改的变量值。volatile的开销更低,且不会引起线程上下文的切换。 1.2 volatile的定义: 如果一个变量被声明为v...

weixin_33696822的博客 141

volatile底层实现原理和其应用

volatile时轻量级的synchronized,它在多处理器开发中保证了数据的读的一致性,意思就是当一个线程修改一个共享变量时,另外一个线程能读到这个共享变量的值。如果volatile变量修饰符使用的恰当的话,他的运行成本会大大降低,因为他不会引起上下文的切换和调度,因为他并不会阻塞线程,也因此他不能保证多个线程对数据进行写操作时的安全性(即原子性)。 volatile不能保证原子性: 所...

qq_39429962的博客 1886

总结一下这段时间看的一下锁

1.什么是java内存模型? 也就是JMM,分为主内存和本地内存(也就是主内存的副本),主内存是共享的,而本地内存是当前线程可见。 2.ThreadLocal的作用是什么? 作用是当前线程的共享变量工具,原理是一个ThreadLocalMap,将线程和对应的变量值保存在里面。 3.ThreadLocal的内存泄漏是什么? ThreadLocal在ThreadLocalMap中以一个弱引用的身份被E...

Ryan_5410的博客 223

2021年 6月7月面试题,持续更新,欢迎大家一起讨论

2021年 6月7月面试题,欢迎大家一起讨论 并发 synchronize升级 volitle 读屏障写屏障怎么加 单例模式实现 懒汉饿汉模式区别,双向验证,会不会有其他方式获取实例 concurrenthashmap 1.7-1.8实现 线程模型 ThreadLocal 底层怎么实现的 ReentrantLock 底层实现 AtomicInteger 底层怎么实现 jdk源码 hashmap扩容 hashmap 17-1.8区别 队列 mq,怎么保证不丢失,保证顺序 canel部署 Redis

lvxy_work的博客 141

volatile 与 synchronize 详解

Java支持多个线程同时访问一个对象或者对象的成员变量,由于每个线程可以拥有这个变量的拷贝(虽然对象以及成员变量分配的内存是在共享内存中的,但是每个执行的线程还是可以拥有一份拷贝,这样做的目的是加速程序的执行,这是现代多核处理器的一个显著特性),所以程序在执行过程中,一个线程看到的变量并不一定是最新的。 volatile 关键字volatile可以用来修饰字段(成员变量),就是告知程序任何对该变量...

Woo_home的博客 2万+
上一篇: 超详细的VMware虚拟机安装CentOS7教程
下一篇: 基于Hexo搭建个人博客优化(五)---压缩篇(gulp4.0压缩静态资源)
探索云原生
博客等级 码龄9年 730粉丝 131原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值