volatile的内存语义

本文详细探讨了Java中volatile变量的内存语义,包括其特性、写-读的happens-before关系以及内存操作的含义。volatile确保了线程间的可见性和单一变量的原子性,但不保证复合操作的原子性。通过对volatile的读写操作,线程间能实现通信,其内存语义等同于锁的释放与获取。
摘要由CSDN通过智能技术生成

理解volatile特性的一个好方法是把volatile变量的单个读/写.看成是使用同一个锁对这些单个读/写操作做出了同步。其实,volatile的读写操作与锁的获取与释放有这对应的关系,我们来慢慢细看,首先看一下volatile的特性:

volatile的特性

class VolatileFeaturesExample{
	 volatile long v1 = 0l;
	 public void set(long l){
		v1 = l;                        // 单个volatile变量的写
	 }
	 public void getAndIncrement(){
		v1 ++;                         // 复合 volatile变量的读/写
	 }
	 public long get(){
		return v1;                      // 单个volatile变量的读
	 }
}

假设有多个线程分别调用上面的3个方法,这个程序在语义上与下面的程序等价:

class VolatileFeaturesExample{
	 volatile long v1 = 0l;
	 public synchronized void set(long l){
		v1 = l;
	 }
	 public void getAndIncrement(){      // 普通方法调用
		long temp = get();              // 调用已同步的读方法
		temp += 1l;                      // 普通写操作
		set(temp);                       //调用已同步的写方法
	 }
	 public synchronized long get(){      // 对单个的普通变量的读写用同一个锁同步
		return v1;
	 }
}

如上面的实例程序所示,一个volatile变量的读写操作,与一个普通变量的读写操作都是使用同一个锁来同步,他们之间的执行效果相同。

锁的happens-before规则保证了释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总能看到(任意线程)对这个volatile变量最后的写。锁的语义决定了临界区执行的原子性,这意味着64位Long类型和double类型变量,只要他是volatile变量,对该变量的读写具有原子性。如果是多个volatile操作或类似于volatile++这种复合操作,这种操作整体上不具有原子性。简言之,volatile变量自身具有以下特性:

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

volatile写-读建立的happens-before关系

在JDK 5开始,volatile变量的写-读可以实现线程的通信。从内存语义的角度来说,volatile的写-读与锁的释放-获取有相同的内存效果:volatile的写与锁的释放具有相同的内存语义,volatile读操作与锁的获取具有相同的内存语义。这在文章的开头以及说到了。

class VolatileExample{
  int a = 0;
  volatile boolean flag = false;
  public void writer(){
	a = 1 ;             // 1
	flag = true;         // 2
  }
  public void reader(){
	if(flag) {            //3
		int i = a;       // 4
	}
  }
}

假设线程A执行writer方法之后,线程B执行reader方法,根据happens-before规则,建立的happens-before关系如下:

  • 根据程序次序规则: 1 happens-before 2 ,3 happens-before 4;
  • 根据volatile规则: 2 happens-before 3;
  • 根据happens-before规则的传递性: 1 happens-before 4 ;

黑色箭头表示程序顺序规则;红色箭头表示volatile规则;蓝色箭头表示组合这些规则后提供的happens-before保证;

volatile 写-读的内存语义

写语义为:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。就如上面的代码来说:线程A在写flag变量后,本地内存A中被线程A更新过的两个共享变量的值会被刷新到主内存中,此时,本地内存A和主内存中的共享变量的值是一致的。读语义是:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

下面我们对volatile写和volatile读的内存语义做一下总结:

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

当然了,这里的发送消息只是逻辑上的,便于理解;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值