`
noble510520
  • 浏览: 53644 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Volatile实现原理

    博客分类:
  • java
阅读更多

 读写volatile变量就像是访问一个同步块一样,是原子的且是可见的,总是能访问到最新的值。

原子性

 读写volatile变量是原子操作,但读写变量不就是一条指令的事吗(mov、ldr),难道这还可分?没错绝大多数变量读写都是原子的,除了在32位JVM下对long、double的读写,就不是原子的。这是因为在32位下,总线宽度就只有32bit,对64位数据的读写需要分两次进行,依次读写高低32位。但是读写volatile变量由于使用了LOCK前缀指令,锁住了内存,所以即使是64位的数据也是原子的。

读写volatile变量是原子的,包括64位的long和double

实现原子性

 实现64位的原子性,需要在读写volatile变量时,使用Lock前缀指令,其作用有:

  1. 锁住该内存地址,直到读完/写完,保证64位变量读写原子性。少量处理器是使用锁总线实现的,相比锁内存,其开销更大,锁总线期间,所有处理器都不能操作主存外存。
  2. 将写缓存刷新到主存,保证可见性
  3. 禁止该指令与前面和后面的读写指令重排序,保证happens-before关系

可见性

happens-before中定义了:写volatile变量,happens-before后面任意一个读这个volatile变量的操作

 这意味着volatile变量在多线程间具有可见性从源码到Runtime发生的重排序指出重排序破坏了可见性。为实现volatile的可见性,读写volatile时则需要禁止重排序,那么需要禁止编译器重排序处理器重排序

happens-before关系

happens-before规则

  1. 程序顺序规则:在一个线程中,前面的操作happens-before后面的操作
  2. volatile写-读规则:写volatile变量,happens-before后面任意一个读这个volatile变量的操作
  3. 传递性规则:A happens-before B,B happens-before C,则A happens-before C

 从这段代码看看happens-before关系,线程A先执行store(),线程B后执行load()

int value = 0;
volatile boolean finish = false;

voidstore(){
    value = 1;      //A
    read(value);    //B
    finish = true;  //C
    value = 2;      //D
    read(value);    //E
}

voidload(){
    value = 3;      //F
    read(value);    //G
    while(!finish); //H
    assert value == 1;  //I
    value = 4;      //J
}

 ①~⑧是程序顺序规则,⑨是volatile写-读规则,浅色的是传递性规则,后面详细解释这些关系。

happens-before关系

从happends-before规则分析可见性

①~⑧是根据程序顺序规则得出的,程序顺序规则前提是仅考虑本线程的可见性,那么就不需要考虑多个处理器引发的缓存不一致问题,不需要考虑内存系统重排序,所以不需要用到内存屏障。这样就很简单了,只要保证其在单线程内运行结果不变即可,只要保证编译器、处理器不重排数据依赖的指令

是根据volatile域写-读规则得出的得出:C happens-before H。也就是线程A写volatile happens-before 线程B读volatile

 再根据传递性规则得出:ABC happens-before H 。也就是线程A写volatile及其之前的操作 happens-before 线程B读volatile

 再根据传递性规则得出:ABC happens-before HIJ 。最终得出线程A写volatile及其之前的操作 happens-before 线程B读volatile及其后续操作

 这样来看,写volatile时,需要马上将本地内存刷新到主存中去。读volatile时,需要将本地内存中共享变量设为无效状态,重新从主存中读。

编译器层面实现可见性

编译器处理volatile变量重排序规则表
可以看到:

  • 写volatile变量时,无论前一个操作是什么,都不能重排序(①~④happens-before)
  • 读volatile变量时,无论后一个操作是什么,都不能重排序(⑤~⑧happens-before)
  • 当先写volatile变量,后读volatile变量时,不能重排序(⑨happens-before)

处理器层面实现可见性

根据前面的出来的可见性:线程A写volatile及其之前的操作 happens-before 线程B读volatile及其后续操作

 可以看到这个可见性是在多线程间的,所以要避免内存系统重排序,需要使用JMM提供的内存屏障

内存屏障

 先给可见性拆分,方便从最简单的开始实现:

  1. 线程A写volatile happens-before 线程B读volatile
  2. 线程A写volatile及其之前的操作 happens-before 线程B读volatile
  3. 线程A写volatile及其之前的操作 happens-before 线程B读volatile及其后续操作

 实现可见性:

  1. 对第一级可见性,可以在写volatile之后加StoreLoad Barrier,也可以在读volatile之前加StoreLoad Barrier。选择哪种?在实际开发中,常用的模式是一个写线程,多个读线程,典型的有生产者消费者模式,所以在写volatile后加StoreLoad Barrier,会大大减少执行屏障的次数,比后者的性能要好。
  2. 对第二级可见性,在写volatile之前加上StoreStore Barrier,可以保证写volatile之前,其之前的所有操作结果已经可见。不用LoadStore Barrier的原因是:读操作并不会改变操作结果。
  3. 对第三级可见性,实际上是保证读volatile后续操作会不会和读volatile重排序。那么就在读volatile后面加LoadLoad Barrier,这样保证读volatile在其后续读操作之前执行,这样的话 线程A 对 读volatile的后续读操作也可见。同理为了使线程A 对 读volatile后续写操作可见,在读volatile后加上LoadStore Barrier。

 综上所述:

  1. 写volatile之前,StoreStore Barrier
  2. 写volatile之后,StoreLoad Barrier
  3. 读volatile之后,LoadLoad Barrier 和 LoadStore Barrier

 在刚才的例子上添加内存屏障,实现happens-before关系。

int value = 0;
volatile boolean finish = false;

voidstore(){
    value = 1;      //A
    read(value);    //B
    storeStoreBarrier();
    finish = true;  //C
    storeLoadBarrier();
    value = 2;      //D
    read(value);    //E
}

voidload(){
    value = 3;      //F
    read(value);    //G
    while(!finish); //H
    loadLoadBarrier();
    loadStoreBarrier();
    assert value == 1;  //I
    value = 4;      //J
}
 
 
1
0
分享到:
评论

相关推荐

    深入分析Volatile的实现原理

    本文将深入分析在硬件层面上Inter处理器是如何实现Volatile的,通过深入分析能帮助我们正确的使用Volatile变量。

    java 高并发中volatile的实现原理

    主要介绍了java 高并发中volatile的实现原理的相关资料,在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”,需要的朋友...

    JMM(Java内存模型)及Volatile底层实现原理.md

    Java内存模型及Volatile底层实现原理

    聊聊并发(一)深入分析Volatile的实现原理

    NULL 博文链接:https://kiral.iteye.com/blog/1416354

    Java中volatile关键字实现原理

    本文详细解读一下volatile关键字如何保证变量在多线程之间的可见性,对Java中volatile关键字实现原理感兴趣的朋友一起通过本文学习吧

    简单了解java volatile关键字实现的原理

    主要介绍了简单了解volatile关键字实现的原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    Java Volatile关键字实现原理过程解析

    主要介绍了Java Volatile关键字实现原理过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    synchronized ReentrantLock volatile Atomic 原理分析.docx

    原理 synchronized关键字是通过字节码指令来实现的 synchronized关键字编译后会在同步块前后形成monitorenter和monitorexit两个字节码指令 执行monitorenter指令时需要先获得对象的锁(每个对象有一个监视器锁...

    深入分析java并发编程中volatile的实现原理

    主要介绍了深入分析java并发编程中Volatile的实现原理,涉及Volatile的官方定义,实现原理,使用优化等相关内容,具有一定参考价值,需要的朋友可以了解下。

    data:Java架构师核心资料持续更新中,需要内推大厂的同学请加微kepeihong

    Java架构师核心资料持续更新中 :bar_chart: :crossed_swords: ...volatile实现原理 消息中间件 Kafka生产者源码分析 Kafka消费者源码分析 Kafka面试题 ActiveMQ面试题 rabbbitmq面试题 消息队列 Java

    Java并发机制的底层实现原理.pdf

    主要总结了:本地内存和线程安全的问题、volatile、synchronized、处理器实现并发的方式、Java实现并发的方式。

    Java并发编程学习笔记

    本文档主要内容如下: 1、线程安全和锁 Synchronized 底层实现原理 2、可重入锁与 Synchronized 的其他特性 3、ThreadLocal 的底层...10、Volatile底层实现原理 11、AQS源码分析 12、CAS原理分析和使用场景 13、.....

    Java并发编程原理与实战

    JDK5提供的原子类的操作以及实现原理.mp4 Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读...

    Java-并发(Concurrent)编程

    volatile关键字及其实现原理 多线程在JVM中的实现原理 JUC概述 原子类Atomic-CAS及其实现原理 锁Lock-AQS核心原理剖析 并发工具类、并发容器、阻塞队列 线程池原理剖析 线程池案例-Web容器-压力测试

    龙果 java并发编程原理实战

    第19节JDK5提供的原子类的操作以及实现原理00:27:10分钟 | 第20节Lock接口认识与使用00:19:54分钟 | 第21节手动实现一个可重入锁00:26:31分钟 | 第22节AbstractQueuedSynchronizer(AQS)详解00:49:04分钟 | 第23...

    聊聊并发系列文章

    1. 聊聊并发(一)深入分析Volatile的实现原理 2. 聊聊并发(二)Java SE1.6中的Synchronized 3. 聊聊并发(三)Java线程池的分析和使用 4. 聊聊并发(四)深入分析ConcurrentHashMap 5. 聊聊并发(五)原子操作的...

    Java 并发编程原理与实战视频

    第19节JDK5提供的原子类的操作以及实现原理00:27:10分钟 | 第20节Lock接口认识与使用00:19:54分钟 | 第21节手动实现一个可重入锁00:26:31分钟 | 第22节AbstractQueuedSynchronizer(AQS)详解00:49:04分钟 | 第23...

    深入理解Java并发之synchronized实现原理.docx

    线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。...完全可以替代Volatile功能),这点确实也是很重要的。

    android IntentService实现原理及内部代码分享

    Android123在早期的文章中已经说明他们的用法,这里不再赘述,有关原理大家可以看源码实现如下: 代码如下:public abstract class IntentService extends Service { private volatile Looper mServiceLooper;...

Global site tag (gtag.js) - Google Analytics