volatile的内存语义

当声明一个volatile变量时,对这个变量的读/写会变得很特别。现在我们就来揭开volatile的神秘面纱。

    volatile自身具有以下特性:

  •    可见性。对一个volatile变量的读,总是能看到任意线程对这个volatile变量最后的写入。
  •    原子性。对任意一个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

但volitile对线程内存可见性的影响比volatile自身的特性更为重要,也更需我们去关注。

    volatile写的内存语义如下:

    当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值全部刷新到主内存。

    volatile读的内存语义如下:

    当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中去读取共享变量。

总结:

也就是说A线程写一个volitile变量后,B线程读取同一个volatile变量,在A线程写volitle操作之前的任何共享变量,在B线程读取同一个volatile变量之后,将立刻对B线程可见。

举一个例子:

public class VolatileExample {

	private volatile boolean flag=false;
	private int a=0;
	
	public void write(){ 
		a=1;             //1
		flag=true;       //2
	}
	
	public void read(){
		if(flag){
			int y=a;  //3
			System.out.println(y);   //4     输出1
		} 
	}
}   

在这段代码里面,根据happen-before规则,这个过程建立的happen-before规格如下:

1.根据程序次序规则:1 happen-before 2,3 happen-before 4;

2.根据volatile读写规格:2 happen-before 3

3.根据传递性规则:1 happen-before 4;

因此A线程中对a的写,永远对B线程可见。即使a不是volatile变量。这就是flag这个volatile变量对内存可见性的作用,当对一个线程对volatile变量进行写操作时,会把该线程本地内存中的共享变量全部刷新到主内存中去,当一个线程读一个volatile变量时,JMM会将该线程的本地内存置为无效,因此线程会去共享内存中读取变量。所以在上面例子中,B线程读取a的值,A已经刷新到主内存中的新值1。


对volatile的内存语义进行总结:

  • 线程A写一个volatile变量,实际上是线程A向接下来要读这个volatile变量的某个线程发出了(其对共享变量所做修改的)消息。
  • 线程B读一个volatile变量,实际上是接收了之前某个线程所发出的(在写这个volatile变量之前对共享变量所做修改的消息)
  • 线程A写一个volatile变量,随后线程B读一个volatile变量,这个过程实际上就是线程A通过主内存向线程B发送消息。

猜你喜欢

转载自blog.csdn.net/fanxin_i/article/details/79891502
今日推荐