问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

实例分析JMM和Volatile的底层原理

创作时间:
作者:
@小白创作中心

实例分析JMM和Volatile的底层原理

引用
1
来源
1.
http://www.cdweb.net/article/psjppi.html

在Java并发编程中,JMM(Java Memory Model)和volatile关键字是两个非常重要的概念。本文将通过实例分析,深入探讨它们的底层原理,帮助读者更好地理解Java内存模型和线程安全机制。

JMM和volatile分析

1. JMM:Java Memory Model,java线程内存模型

JMM是一个抽象的概念,描述的是线程和内存间的通信。它类似于CPU缓存模型,是一个标准化的模型,用于屏蔽硬件和操作系统对内存访问的差异性。

2. JMM和8大原子操作结合

虽然文中没有详细展开这部分内容,但JMM定义了8个原子操作,这些操作是线程间通信的基础。

3. volatile的应用及底层原理探究

volatile关键字可以看作是轻量级的synchronized,主要用于保证共享变量的"可见性"。当一个线程修改了某个共享变量时,其他使用到该共享变量的线程能够及时读取到修改的值。与synchronized相比,volatile的执行成本更低,因为它不会引起线程上下文切换和调度。

下面通过一个简单的代码示例来说明volatile的使用:

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编译器生成的汇编指令。以下是查看步骤:

  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

下面是带有volatile修饰的代码生成的汇编指令:

0x000000000f11ac90: lock add dword ptr [rsp],0h ;*putstatic flag ; - com.yew.test.VolatileTest::update@1 (line 17)

而没有volatile修饰的代码生成的汇编指令则没有lock前缀:

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(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致性。通过嗅探技术保证处理器内部缓存、系统缓存和其他处理器缓存的数据再总线上保持一致。当其他处理器打算回写内存地址,该地址是共享内存区域,那么嗅探的处理器会将它的缓存行设置为无效,下次访问相同内存时,强制执行缓存行填充。

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

通过本文的分析,相信读者对JMM和volatile的底层原理有了更深入的理解。掌握这些知识对于编写高效、安全的并发程序至关重要。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号