1 public class VolatileTest2 implements Runnable{ 2 volatile int resource = 0; 3 4 public static void main(String[] args) { 5 VolatileTest2 vt = new VolatileTest2(); 6 new Thread(vt).start(); 7 new Thread(vt).start(); 8 while(Thread.activeCount() > 1){ 9 Thread.yield(); 10 } 11 System.out.println(vt.resource); 12 } 13 14 @Override 15 public void run() { 16 for(int i=0; i<100000000; i++){ 17 resource++; 18 } 19 } 20 }
这个例子中虽然resource前面有volatile关键字,但是运行结果有时候仍然不是200000000..为什么呢? 我想分享下我的观点.
首先每个线程有自己的内存,他们修改resource的时候会先把数据拷贝到自己的内存中,再修改,再写回主内存..所以如果是这样的话那么多个线程同时操作可能会有很多种情况,下面举2种情况
(不加volatile关键字的时候会出现的N种情况中的2种)
第一种情况:
第二种情况
上面2种情况都会使t2线程把resource写回主内存的时候把t1自增那次操作覆盖掉.因为t1与t2线程读主内存的resource的值是一样的...所以相当于t1的自增是无效的.
当resource前面加了volatile关键字的时候:
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值。
http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
所以加了这个关键字以后是可以避免第二种情况的.
t1 resource自增以后会直接写回主内存,使t2读取操作读取到的是最新的值.相当于resource++与写回主内存是同一个事务,不可以分隔.
但是仍然不能避免第一种情况的发生.
在第一种情况下,线程t2 读取resource是发生在t1 resource自增之前的,所以当t1 自增以后并不会影响t2线程内存中的resource,因为这并不违背volatile.如果此时t2再去读取resource那才是最新的值.不过可惜他在t1自增之前就读取了,所以t2中resource的值仍然是旧的.当t2写回主内存的时候仍然会覆盖t1自增的值.
以上就是我的理解.