通过实例解析JMM和Volatile底层原理
作者:VincentYew 发布时间:2023-05-20 19:10:48
这篇文章主要介绍了通过实例解析JMM和Volatile底层原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
JMM和volatile分析
1.JMM:Java Memory Model,java线程内存模型
JMM:它是一个抽象的概念,描述的是线程和内存间的通信,java线程内存模型和CPU缓存模型类似,它是标准化的,用于屏蔽硬件和操作系统对内存访问的差异性。
2.JMM和8大原子操作结合
3.volatile的应用及底层原理探究
volatile : 轻量级的synchronized,在多处理器的开发中保证了共享变量的"可见性"。可见性的意思:当一个线程修改了某个共享变量时,其他使用到该共享变量的线程能够及时读取到修改的值。修饰得当,比synchronized的执行成本更低,因为它不会引起线程上下文切换和调度。
public class VolatileTest {
private static volatile boolean flag = false;
public static void main(String[] args) {
update();
}
public static void update(){
flag = true;
System.out.println(flag);
}
}
Volatile JIT编译器编译java代码为汇编指令查看
1.在jdk\jre\bin\ 目录下添加 hsdis-amd64.lib
2.在jdk1.8\jre\bin\server\目录下添加hsdis-amd64.dll文件
3.在IDEA中设置 JVM参数
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,VolatileTest.update
4.运行Java程序即可打印出
CompilerOracle: compileonly *VolatileTest.update
Loaded disassembler from E:\EclipseDev\jdk\jdk1.8\jre\bin\server\hsdis-amd64.dll
Decoding compiled method 0x000000000f11aad0:
Code:
Argument 0 is unknown.RIP: 0xf11ac40 Code size: 0x000002a8
[Disassembling for mach='amd64']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest'
# [sp+0x40] (sp of caller)
0x000000000f11ac40: mov dword ptr [rsp+0ffffffffffffa000h],eax
0x000000000f11ac47: push rbp
0x000000000f11ac48: sub rsp,30h
0x000000000f11ac4c: mov r8,8792d70h ; {metadata(method data for {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest')}
0x000000000f11ac56: mov edx,dword ptr [r8+0dch]
0x000000000f11ac5d: add edx,8h
0x000000000f11ac60: mov dword ptr [r8+0dch],edx
0x000000000f11ac67: mov r8,8792b70h ; {metadata({method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest')}
0x000000000f11ac71: and edx,0h
0x000000000f11ac74: cmp edx,0h
0x000000000f11ac77: je 0f11ad68h ;*iconst_1
; - com.yew.test.VolatileTest::update@0 (line 17)
0x000000000f11ac7d: mov r8,0d7b08a30h ; {oop(a 'java/lang/Class' = 'com/yew/test/VolatileTest')}
0x000000000f11ac87: mov edx,1h
0x000000000f11ac8c: mov byte ptr [r8+68h],dl
volatile修饰
0x000000000f11ac90: lock add dword ptr [rsp],0h ;*putstatic flag
; - com.yew.test.VolatileTest::update@1 (line 17)
无Volatile修饰
0x000000000f113707: mov byte ptr [r8+68h],1h ;*putstatic flag
; - com.yew.test.VolatileTest::update@1 (line 17)
通过比较可知:改变共享变量flag的值为true,该变量由Volatile修饰,进行汇编打印时,会有lock前缀修饰,根据IA-32架构软件开发者手册可知,lock前缀指令在多核CPU处理器下会引发两件事情:
【1】将当前处理器缓存行的数据立即写回系统内存
【2】wirte操作会使其他处理器中缓存该内存地址的数据无效
LOCK#声言期间,处理器独占任何共享内存。IA-32处理器和Intel 64处理器使用MESI(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致性。通过嗅探技术保证处理器内部缓存、系统缓存和其他处理器缓存的数据再总线上保持一致。当其他处理器打算回写内存地址,该地址是共享内存区域,那么嗅探的处理器会将它的缓存行设置为无效,下次访问相同内存时,强制执行缓存行填充。
0x000000000f11ac95: nop
0x000000000f11ac98: jmp 0f11add4h ; {no_reloc}
0x000000000f11ac9d: add byte ptr [rax],al
0x000000000f11ac9f: add byte ptr [rax],al
0x000000000f11aca1: add byte ptr [rsi+0fh],ah
0x000000000f11aca4: Fatal error: Disassembling failed with error code: 15Decoding compiled method 0x000000000f11ef50:
Code:
Argument 0 is unknown.RIP: 0xf11f080 Code size: 0x00000058
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest'
# [sp+0x20] (sp of caller)
0x000000000f11f080: mov dword ptr [rsp+0ffffffffffffa000h],eax
0x000000000f11f087: push rbp
0x000000000f11f088: sub rsp,10h
0x000000000f11f08c: mov r10,0d7b08a30h ; {oop(a 'java/lang/Class' = 'com/yew/test/VolatileTest')}
0x000000000f11f096: mov byte ptr [r10+68h],1h
0x000000000f11f09b: lock add dword ptr [rsp],0h ;*putstatic flag
; - com.yew.test.VolatileTest::update@1 (line 17)
0x000000000f11f0a0: mov edx,1ch
0x000000000f11f0a5: nop
0x000000000f11f0a7: call 0f0557a0h ; OopMap{off=44}
;*getstatic out
; - com.yew.test.VolatileTest::update@4 (line 18)
; {runtime_call}
0x000000000f11f0ac: int3 ;*getstatic out
; - com.yew.test.VolatileTest::update@4 (line 18)
0x000000000f11f0ad: hlt
0x000000000f11f0ae: hlt
0x000000000f11f0af: hlt
0x000000000f11f0b0: hlt
0x000000000f11f0b1: hlt
0x000000000f11f0b2: hlt
0x000000000f11f0b3: hlt
0x000000000f11f0b4: hlt
0x000000000f11f0b5: hlt
0x000000000f11f0b6: hlt
0x000000000f11f0b7: hlt
0x000000000f11f0b8: hlt
0x000000000f11f0b9: hlt
0x000000000f11f0ba: hlt
0x000000000f11f0bb: hlt
0x000000000f11f0bc: hlt
0x000000000f11f0bd: hlt
0x000000000f11f0be: hlt
0x000000000f11f0bf: hlt
[Exception Handler]
[Stub Code]
0x000000000f11f0c0: jmp 0f0883a0h ; {no_reloc}
[Deopt Handler Code]
0x000000000f11f0c5: call 0f11f0cah
0x000000000f11f0ca: sub qword ptr [rsp],5h
0x000000000f11f0cf: jmp 0f057600h ; {runtime_call}
0x000000000f11f0d4: hlt
0x000000000f11f0d5: hlt
0x000000000f11f0d6: hlt
0x000000000f11f0d7: hlt
true
4.volatile的使用优化
java并发大师Doug Li在jdk7并发包中新增了一个队列集合LinkeTransferQueue,它在使用Volatile关键字修饰变量时,采用追加字节的方式将变量填充到64字节
volatile修饰变量在进行修改时,会进行LOCK前置指令加锁,锁住缓存行的数据独占
适用于:缓存行字节为64字节 处理器如 I7 酷睿 Pentium M等
不适用:非64字节宽的缓存行 P6系列或者奔腾 共享变量不会被频繁的写
5.并发编程的三大特性:可见性、原子性、有序性
volatile可以保证可见性、有序性,但是不保证原子性。
6.volatile关键字的语义分析
(1)保证可见性,volatile修饰的共享变量被修改时,其他处理器能立刻嗅探到共享变量值的改变
(2)保证有序性:根据happens-before原则可知,当变量使用volatile修饰时,程序代码前后的位置不能发生指令重排和提取。
(3)volatile底层采用汇编的lock前缀指令锁定共享变量内存地址的缓存行,从而控制并发的安全性(轻量级synchronized)
7.volatile使用场景以及和synchronized的区别
使用场景:1.标志状态 2.DCL--双重检测锁(单例模式) 3.保证可见性、顺序性
区别:
1.使用上:volatile修饰变量 synchronized修饰方法或者代码块
2.原子性的保证 volatile不保证原子性 synchronized可以保证原子性
3.可见性保证机制不同 volatile通过汇编的lock前缀指令 synchronized使用Monitor属性(Moniterentet 入口 Moniterexit--出口(包含异常))
4.有序性保证的锁的粒度 volatile粒度小,synchronized粒度大
5.其他 volatile不会引起线程阻塞 synchronized会引起线程的阻塞
来源:https://www.cnblogs.com/vincentYw/p/12040336.html


猜你喜欢
- float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal。而且使用BigDe
- java多线程之CyclicBarrier的使用方法public class CyclicBarrierTest { public stat
- 抽象类1.引出抽象类向上转型带来的最大的好处就是参数统一化,使用共同的父类引用,就可以接收所有的子类实例。多态非常依赖方法覆写,但是子类可以
- 前言前面文章我们介绍了Broker是如何将消息全量存储到CommitLog文件中,并异步生成dispatchRequest任务更新Consu
- 给新建的winform程序添加资源文件夹Resources小菜鸟开始学习WinForm程序别人的项目都有资源文件夹放图片之类的,我的就是没有
- 实践过程效果代码public partial class GlorifyCheckBox : CheckBox {
- 众所周知,一般情况下我们使用android中的monkeyrunner进行自动化测试时,使用的是python语言来写测试脚本。不过,最近发现
- 本节作为主要讲解Spring Data的环境搭建JPA Spring Data :致力于减少数据访问层(DAO)的开发量。开发者唯一要做的就
- C sharp (#) 数据类型获取这里研究一下关于c#中如何获取变量类型的问题。首先我们研究一下如何获取单个变量的类型// 问题一:获取单
- JSR303介绍在Java中提供了一系列的校验方式这些校验方式在javax.validation.constraints包中引入依赖<
- 本文实例讲述了基于私钥加密公钥解密的RSA算法C#实现方法,是一种应用十分广泛的算法。分享给大家供大家参考之用。具体方法如下:一、概述RSA
- 提供表示 Windows 注册表中的根项的 RegistryKey 对象,并提供访问项/值对的 static&
- 要完成一个轮播图片,首先想到的应该是使用ViewPager来实现。ViewPager已经有了滑动的功能,我们只要让它自己滚动。再加上下方的小
- 本文为大家分享了java实现百度云OCR识别的具体代码,高精度OCR识别身份证信息,供大家参考,具体内容如下1.通用OCR文字识别这种OCR
- Java中IO的模型分为三种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。BIO【同步阻塞】在JDK1.4出来之前,我们建立网
- 1、准备使用redis实现分布式锁,需要用的setnx(),所以需要集成Jedis需要引入jar,jar最好和redis的jar版本对应上,
- java 中cookie的详解Java对cookie的操作比较简单,主要介绍下建立cookie和读取cookie,以及如何设定cookie的
- java url中如何传递数组,springMVC框架controller类如何接收数组参数?下面介绍一下URL中传递数组参数方法:dd.d
- unity贪吃蛇基本原理实现,供大家参考,具体内容如下原理:1、每个身体跟着前面的身体移动;2、蛇头自动一直向前走,可以向左或者向右转弯。思
- 网上关于java图片格式内容的文章不是很多,也不是很完整,小编搜集了三段java图片格式转换代码,分享给大家:第一段:java图片格式转换代