Java并发编程之volatile

Java修饰一个变量为volatile后,其就具备2种特性:

1.内存可见性

2.禁止指令重排序优化

 

一、内存可见性是什么呢?

volatile是保证共享变量变化可见性的关键字。所谓可见性,就是一个变量在多个线程间可见,而volatile保证了共享变量的改变在多线程之间能够及时的发现。

CPU有多个核(线程),每个核(线程)都有一个叫缓冲区(cache),多线程运行程序时每个核的cache就可能发生不同步的情况。

例如:线程(A和B)在运行程序时,线程(A和B)都会从主内存中copy一份变量副本到各自的缓冲区中。当线程B对变量进行了修改操作,这时线程A因为已经copy了一份变量副本到自己缓冲区,对于线程B对变量进行了修改,线程A是不知道变量已经修改了。线程A还是从自己的缓冲区取数据。

package thread.com.cn;

import java.util.concurrent.TimeUnit;

public class VolatileTest {
	/* volatile */ boolean flag = true;

	void m() {
		System.out.println("m start...");
		while (flag) {
		}
		System.out.println("m end...");
	}

	public static void main(String[] args) {
		VolatileTest t = new VolatileTest();
		
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t.flag = false;
	}
}

运行结果:

1.CPU非空间情况下就会一直打印m start...

2.CPU在非常空闲的情况下,打印结果就会是:m start...和m end...

使用volatile修饰的变量不允许线程(核)内部缓存和重排序,直接修改内存,对于其他线程是可见的。

声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。

 

二、指令重排是什么呢?

是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。

Test t = new Test()

上面的代码虽然只有一行,但不是原子操作,它分成3步骤:

1.开辟内存空间

2.初始化对象

3.把初始化的对象指向内存地址上

new 对象Test时3步操作的执行顺序是不一定的,有可能是132或123。

如果是132这样,在13之后程序意外终止,t != null,但是 t 还没初始化完成

如果加了volatile关键字就禁止了指令重排。程序是123这样执行,如果12或1执行了之后程序意外终止,t还是null。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章