JAVA-并发编程-volatile解读

volatile的特性

理解volatile特性的一个好方法就是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。

package text1;

public class Threadtest implements Runnable{
	int v1 = 0;
	public synchronized void set(int l) {
		v1 = l;
	}
	
	
	public synchronized int get() {
		return v1;
	}
	public void getAndIncrement() {
		int temp = get();
		temp += 1L;
		set(temp);
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0; i < 100000; i++)
			getAndIncrement();
		System.out.println(Thread.currentThread().getName()+"::"+v1);
	}
	
	public static void main(String[] args) throws InterruptedException {
		duoxiancheng t1 = new duoxiancheng();
		Thread t3 = new Thread(t1);
		Thread t4 = new Thread(t1);
		t3.start();
		t4.start();
	}
	
}

如上所示只要是volatile变量,对该变量的读/写就具有原子性。

简而言之:volatile变量自身具有以下特性

可见性:对一个volatile变量的读,总能看到(任意线程)对这个volatile变量最后的写入。(这个就类似于git操作,push之前必须pull一下更新到最新的版本。)
原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

volatile写-读建立的happens关系

volatile对线程的内存可见性的影响比volatile自身的特性更为重要。

从内存语义的角度来讲,volatile的写-读与锁的释放-获取有相同的内存效果:volatile写和锁的释放有相同的内存语义,volatile读与锁的获取有相同的内存语义。

假设线程A执行writer()方法之后,线程B执行reader()方法。根据happens-before规则,这个过程建立的happens-before关系可以分为三类

1.根据程序次序规则,1hanppens-before2;3hanppens-before4.
2.根据volatile规则,2hanppens-before3.
3.根据hanppens-before的传递性规则1hanppens-before4。

package text1;

public 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
		}
	}
}

volatile写-读的内存语义

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

当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。也就是说,比如上面那个程序为例,当一个线程执行writer()方法只后,a和flag的值都变了,而线程B存到本地内存的a和flag的值就失效了。

volatile内存语义的实现

重排序分为编译器重排序和 处理器重排序,前者时在编译时发生的重排序,后者时在程序执行时发生的重排序。

下面时基于保守策略的JMM内存屏障插入策略。

1.在每个volatile写操作的前面插入一个StoreStore屏障(不能同时写)
2.在每个volatile写操作的后面插入一个StoreLoad屏障(volatile写的时候不能读)
3.在每个volatile读操作的后面插入一个LoadLoad屏障(不能同时读)
4.在每个volatile读操作的后面插入一个LoadStore屏障(volatile读的时候不能写)

package text1;

public class VolatileBarrierExample {
	int a;
	volatile int v1=1;
	volatile int v2=2;
	
	void readAndwrite() {
		int i=v1;	//第一个volatile读
		int j=v2;	//第二个volatile读
		a=i+j;	//普通写
		v1=i+1;	//第一个volatile写
		v2=j+2;	//第二个volatile写
	}
	}

在这里插入图片描述
根据上面的图给出总结

volatile读或者写后面时哪种屏障根据本身和后面的操作来定义,比如第一个volatile读后面时volatile读,那么第一个volatile读操作的后面就是LoadLoad屏障(不能同时读)

注意:最后的那个StoreLoad屏障不能省略,编译器无法准确的判断后面是否会有volatile读或者写,为了安全起见,编译器通常会在这里插入一个StoreLoad屏障。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值