美文网首页
Java volatile 整理

Java volatile 整理

作者: SlowGO | 来源:发表于2019-02-07 19:58 被阅读9次

一、volatile 特性

多线程环境下,每个线程都有自己的一个工作内存,对于共享变量,读时是先从主内存加载到工作内存,写时是从工作内存写回主内存。

这个工作内存就相当于一个线程级别的缓存,与其他缓存一样,存在一致性问题,比如一个共享变量的值在一个线程的工作内存中被修改了,那么什么时候同步回主内存呢?这个是没有明确规定的,这就导致了问题,例如多个线程都在自己的工作内存中修改共享变量的值,而主内存中的值是旧的,各个线程也不知道其他线程对变量的改动,这就是不可见问题。

volatile可以保证:

  • 每次读取volatile变量时都是从主内存中读的。
  • 每次写volatile变量时都会写回主内存。

从而,volatile 解决了可见性问题,每次读写都是直接关联主内存的,这是 volatile 的首个重要特性。还有一个重要特性是防止指令重排序,具体的后面再说。

小结一下volatile的特性:

  1. 保证了共享变量在多线程下的可见性。
  2. 防止指令重排序。

二、volatile 是怎么保证可见性的?

1. 什么是可见性问题?

多线程应用中,为了性能,每个线程都会把共享变量从主内存拷贝到自己的工作内存中,多cpu计算机中每个线程运行在不同的cpu中,每个线程就把共享变量拷贝到自己的cpu cache中。

image

对于普通变量,JVM什么时候将其从主内存读到 cpu cache 中?以及什么时候从 cpu cache 写回主内存?这些都是没有保证的。

public class SharedObject {

    public int counter = 0;

}

如这段代码,假设线程1对counter执行增加操作,线程1和线程2都会时不时的读取 counter,线程1的操作结果什么时候写回主内存是没谱的,就会发生下图中的情况:

image

可见性问题:一个线程修改了变量值,由于还没有写回主内存,导致其他线程看不到最新值,也就是一个线程的更新对其他线程不可见。

2. volatile 对可见性的保证

变量声明了 volatile 之后,所有对其写后都会立即写回主内存,所有对其的读操作都会直接从主内存中读。

大概原理:

多处理器下,会实现一个缓存一致性协议,每个处理器通过分析在总线上传播的数据来检查自己缓存的值是否过期了,所以当 volatile 变量值写回到主内存后,其他处理器会发现自己缓存行对应的内存地址被修改了,就会将当前缓存置为无效状态,所以当再次操作这个变量时就得从主内存重新读取到缓存,从而拿到最新值。

3. 全可见性保证

事实上,volatile 对可加性的保证已经超过了 volatile 变量本身,还遵循如下原则:

  • 如果线程A对一个volatile变量进行写入,并且线程B接下来对这个变量进行读取,那么在写volatile变量前对线程A可见的所有变量,在线程B读取volatile变量之后这些变量对线程B也是可见的。
  • 如果线程A读取了一个volatile变量,在读取之后,线程A中所有可见变量都会从主内存重新读取。

示例代码:

public class MyClass {
    private int years;
    private int months
    private volatile int days;


    public void update(int years, int months, int days){
        this.years  = years;
        this.months = months;
        this.days   = days;
    }
}

udpate()写了3个变量,其中只有 daysvolatile

days写入后,所有可见变量(years、months)也会写入主内存。

读取的示例代码:

public class MyClass {
    private int years;
    private int months
    private volatile int days;

    public int totalDays() {
        int total = this.days;
        total += months * 30;
        total += years * 365;
        return total;
    }

    public void update(int years, int months, int days){
        this.years  = years;
        this.months = months;
        this.days   = days;
    }
}

totalDays()中读取days时,years、months也会从主内存中读取。

三、volatile 是怎么防止重排序的?

为了提高性能,JVM和CPU允许对指令的顺序进行重排,只要保证整体语义和结果是正确的。

例如:

int a = 1;
int b = 2;

a++;
b++;

可以重拍为:

int a = 1;
a++;

int b = 2;
b++;

看下面的代码:

public class MyClass {
    private int years;
    private int months
    private volatile int days;


    public void update(int years, int months, int days){
        this.years  = years;
        this.months = months;
        this.days   = days;
    }
}

update()中写了days,那么years months也都会写入主内存。但如果发生了重拍的话呢,例如:

public void update(int years, int months, int days){
    this.days   = days;
    this.months = months;
    this.years  = years;
}

days的写入被排在了第一行,虽然写入的同时也会把years months的值写入主内存,但之后又修改了years months的值,这时他俩的最新值就不会被马上写入主内存了,这就与重排序之前的代码意义不同了。

为了解决重排序问题,volatile 给出了“happens-before”保证:

  • 对于其他变量的读写,如果本来是在写volatile之前,那么不能被重排到之后。volatile变量写之前的读写操作可以保证发生在写之前。注意,对于本来是在写volatile变量之后的其他变量读写操作是可以被重排到写volatile变量之前的。把后面的重排到前面可以,但把前面的排到后面是不允许的。
  • 对于其他变量的读写,如果本来是发生在读volatile之后的,那么不能被重排到之前。注意,对于本来发生在读volatile之前的其他变量的读可以重拍到之后。把前面的读放到后面可以,但把后面的放到前面不行。

参考:http://tutorials.jenkov.com/java-concurrency/volatile.html

相关文章

  • Java volatile 整理

    一、volatile 特性 多线程环境下,每个线程都有自己的一个工作内存,对于共享变量,读时是先从主内存加载到工作...

  • Java Volatile transient 关键字

    Java Volatile transient 关键字 java关键字volatile Volatile修饰的成员...

  • Volatile理解

    Java Volatile1. volatile 理解2. volatile 不保证原子性3. Volatile ...

  • volatile关键字

    volatile keyword example How to use volatile keyword in java

  • volatile关键字

    java中关键字volatile的作用; volatile vs synchronized的区别 Java并发编...

  • 多线程、并发及线程的基础问题

    一、Java 中能创建 volatile 数组吗? 能,Java 中可以创建 volatile 类型数组,不过只是...

  • Java并发编程系列-volatile

    原创文章,转载请标注出处:《Java并发编程系列-volatile》 一、概述 据说,volatile是java语...

  • java关键字-volatile

    前言 java 5之前这个关键字备受争议,java5只有volatile才得以重生 因为volatile和java...

  • java学习笔记

    1.volatile 2.JMM(java内存模型) 3.volatile代码可见性 4.volatile代码不保...

  • 一、volatile和sychronized

    1 volatile 1.1 volatile的应用 Java语言规范第3版中对volatile的定义如下:Jav...

网友评论

      本文标题:Java volatile 整理

      本文链接:https://www.haomeiwen.com/subject/gkygsqtx.html