首先看一段代码:
package test; /** * VolatileTest : Volatile 关键字测试 * @author xuejupo [email protected] * create in 2016-1-22 下午6:40:19 */ public class VolatileTest { private int number = 0; private int numberVolatile = 0; private static int numberStatic = 0; /** * main: * @param args * void 返回类型 */ public static void main(String[] args) { // TODO Auto-generated method stub VolatileTest test = new VolatileTest(); for(int i = 0; i < 10000; i++){ //100个线程进行数据的++操作 test.new testThread().start(); } //打印最后的结果 System.out.println("普通的number:"+test.number); System.out.println("static类型的number:"+numberStatic); System.out.println("Volatile的number:"+test.numberVolatile); } /** * testThread:测试线程,对Volatile变量执行++操作 * @author xuejupo [email protected] * create in 2016-1-22 下午6:40:47 * */ class testThread extends Thread{ public void run(){ number++; numberStatic++; numberVolatile++; } } }
结果:
普通的number:9995 static类型的number:9995 Volatile的number:9996
可以看到,其实将变量声明成static的,和加上volatile关键字,都不能保证线程安全的。。
volatile是不保证线程安全的。。 他只保证修饰变量的可见性,不保证原子性。 什么意思?我们都知道i++操作不是原子操作,他分3步操作:首先取i,然后执行i+1,然后执行i=i+1, 比如有两个线程t1和t2,t1执行i++的时候,t2恰好也在执行i++,那么他保证t1取到的i和t2取到的i跟主存中的i是一样的,但是在执行i=i+1操作的时候,t2线程可能就把t1线程覆盖了。。
换句话说,volatile关键字保证你的所有线程取到的变量都是最新的,但是不保证正确性。
什么情况下使用volatile关键字?
首先能想到的自然是状态标志位。
打个比方,多个线程执行同一任务,可以设置一个标志位boolean isDown表示是否有线程完成任务。isDown就适合用volatile关键字。因为如果不用volatile,那么单个线程修改变量之后对主存的更新是有延迟的(上面的代码结果中volatile变量比普通的大1,也能说明这一点)。当然,这种延迟大部分情况下是无影响的,但是最好加上volatile关键字。
然后,能想到的适合用volatile关键字的地方就是在上面的代码里,如果对变量的改变只是一个线程,而其他线程都是对变量的读取,并且需要变量的最新值,那么这种情况下个人觉得最适合用volatile关键字修饰。比如说天气预报系统,对天气类的更新可能只有一个线程,而对天气类的读取可能很多个线程并发执行,这种情况下太适合用volatile了。
简而言之,就是volatile保证了数据的一致性,即某线程对变量的更新能马上同步到主存,在原子操作中,他是线程安全的,但是他不保证数据操作的原子性,在非原子性操作中他不是线程安全的(比如i++)