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系列或者奔腾   共享变量不会被频繁的写

4.并发编程的三大特性:可见性、原子性、有序性

volatile可以保证可见性、有序性,但是不保证原子性。

5.volatile关键字的语义分析

(1)保证可见性,volatile修饰的共享变量被修改时,其他处理器能立刻嗅探到共享变量值的改变

(2)保证有序性:根据happens-before原则可知,当变量使用volatile修饰时,程序代码前后的位置不能发生指令重排和提取。

(3)volatile底层采用汇编的lock前缀指令锁定共享变量内存地址的缓存行,从而控制并发的安全性(轻量级synchronized)

5.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会引起线程的阻塞

JMM和Volatile底层原理分析的更多相关文章

  1. volatile底层原理详解

    今天我们聊聊volatile底层原理: Java语言规范对于volatile定义如下: Java编程语言允许线程访问共享变量,为了确保共享变量能够被准确和一致性地更新,线程应该确保通过排它锁单独获得这 ...

  2. 并发之volatile底层原理

    15.深入分析Volatile的实现原理 14.java多线程编程底层原理剖析以及volatile原理 13.Java中Volatile底层原理与应用 12.Java多线程-java.util.con ...

  3. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  4. HashMap底层原理分析(put、get方法)

    1.HashMap底层原理分析(put.get方法) HashMap底层是通过数组加链表的结构来实现的.HashMap通过计算key的hashCode来计算hash值,只要hashCode一样,那ha ...

  5. springAop:Aop(Xml)配置,Aop注解配置,spring_Aop综合案例,Aop底层原理分析

    知识点梳理 课堂讲义 0)回顾Spring体系结构 Spring的两个核心:IoC和AOP 1)AOP简介 1.1)OOP开发思路 OOP规定程序开发以类为模型,一切围绕对象进行,OOP中完成某个任务 ...

  6. Activiti工作流学习笔记(三)——自动生成28张数据库表的底层原理分析

    原创/朱季谦 我接触工作流引擎Activiti已有两年之久,但一直都只限于熟悉其各类API的使用,对底层的实现,则存在较大的盲区. Activiti这个开源框架在设计上,其实存在不少值得学习和思考的地 ...

  7. AQS底层原理分析

    J.U.C 简介 Java.util.concurrent 是在并发编程中比较常用的工具类,里面包含很多用来在并发场景中使用的组件.比如线程池.阻塞队列.计时器.同步器.并发集合等等.并发包的作者是大 ...

  8. volatile的原理分析

    前言:Volatile作为一个多线程开发中的强有力的轻量级的线程协助工具,在实际编程中随处可见,它比synchronized更加轻量和方便,消耗的资源更少,了解Volatile对后面了解多线程有很重要 ...

  9. 基于JAVA Socket的底层原理分析及工具实现

    前言 在工作开始之前,我们先来了解一下Socket 所谓Socket,又被称作套接字,它是一个抽象层,简单来说就是存在于不同平台(os)的公共接口.学过网络的同学可以把它理解为基于传输TCP/IP协议 ...

随机推荐

  1. Java 内建函数式接口

    Java 提供一个包:java.util.function :包中主要提供四个核心接口: 1.功能性接口(Function) public interface Function<T,R> ...

  2. MES系统在小批量电子行业生产管理中的应用

    小批量电子产品生产管理的主要问题 电子电器制造类企业,既有单件小批量生产,也有批量生产:有按库存生产,也有按订单生产,属于典型的离散制造行业.因产品的不同其生产工艺流程也不尽相同,生产设备的布置不是按 ...

  3. 解决iOS地图持续定位耗电问题

    地图位置刷新的代理didUpdateLocations会持续调用,手机非常耗电 但是在实际开发中,有一些APP确实需要用到持续定位的功能,比如:运动类, 导航类, 天气类等等 如何进行持续定位呢?保证 ...

  4. [转]JVM系列二:GC策略&内存申请、对象衰老

    原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html JVM里的GC(Garbage Collection)的算法有 ...

  5. [20190510]rman备份的疑问8.txt

    [20190510]rman备份的疑问8.txt --//上午测试rman备份多个文件,探究input memory buffer 的问题.--//补充测试5个文件的情况.--//http://blo ...

  6. win10安装并激活office

    安装并且激活office 相信很多小伙伴在平时安装office时都会和一些流氓软件做过激烈的斗争,但是还是没有安装成功, 最后完美的安装了一些流氓软件.(苦笑) 下面我们来安装office并且破解 下 ...

  7. webstorm的git操作使用

    0. 前言 在上一篇文章中,讲述了使用webstorm去调试node程序,最近研究了一下如何使用webstorm去操作git. 对于git的使用,大家的使用方式均有不同,最王道的方式非命令行莫属,基于 ...

  8. 10.Java基础_IDEA的使用

    IDEA下项目结构: 项目->模块->包->java文件 流程图 1.创建一个空项目(JavaSE_Code) 选择Empty Project 输入项目名和路径 2.创建一个新模块 ...

  9. Springboot上传图片并访问

    Springboot上传图片并访问 步骤 配置绝对路径,并将这个绝对路径添加到springboot静态资源目录中. 文件上传使用绝对路径保存.返回web相对路径,前端加上域名和项目路径,生成完整的路径 ...

  10. 剑指Offer-11.二进制中1的个数(C++/Java)

    题目: 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 分析: 将数字和1先做与运算,然后将1右移一位,现在是判断数字的第二位是不是1,这样循环的做下去即可.也可以转换成字符串再统计 ...