volatile 据说是 实现 可见 非互斥的行为下面是书上的一个例子 。。public class Testvolatile { private static  boolean flag ;

public static void main(String []str) throws InterruptedException
{
     Thread back = new Thread(new Runnable()
     {
      public void run(){
      int i = 0;
      while(!flag)     
         System.out.println(i++);      
    
      }
     }) ;
     back.start();      
     Thread.sleep(1000);      
     flag = true;
}


}
理论上 应该这个程序一直执行下去 , 因为 主线程修改了flag = true 。但是 线程back应该看不到 。。要把flag 设置为volatile 才可以看到 但是我运行了 这个程序 ,发现不需要volatile 的关键字 ,程序依然会停止 。
何解???

解决方案 »

  1.   

    当然可以不需要,只是这里做不到主线程和back 线程同步而已。
      

  2.   

    这个问题主要是由于这句代码造成的Thread.sleep(1000);  ,你发现没有你把这句话写在了主线程里,并且是第一句,所以程序一定是最新执行Thread.sleep(1000);  这句,当CPU发现需要等待1秒时,它会把执行权交给子线程(该程序中就一个主线程,一个子线程),所以它会执行int i = 0;    while(!flag) 
    System.out.println(i++);   ;因为默认的flag是false,所以必然会执行打印那句代码,当执行了一会(差不多过了1秒,此时已打印了n行,计算机速度好快\(^o^)/~),cpu转而接着执行主线程flag = true;,此时主线程结束,子线程因为flag=true了也执行结束....
    所以就这样,根本无需volatile方法程序自动结束,归根到底是你本身测试方法写的有问题
      

  3.   

    根本问题是HotSpot VM的类型。HotSpot VM的类型有2种:client 和 server。
    您书上的例子一定是运行在sever版本上,而您自己的测试版本在client上。
    client版的实现没有作优化(hoisting)所以没有永不跳出循环的问题。ps:您可以System.out.println(System.getProperty("java.vm.name"));察看当前VM版本。
      

  4.   

    回复3楼的 。。 这代码是Effective Java这本书的一个测试例子 。。  也是很多volatile 介绍的一个例子  volatile一个作用是 代替 synchronized 的关键字 ,在非互斥情况下。一个线程写多个线程读的时候 。 实现多线程的可见 这里面正好是一个线程读,另一个线程写。所以理论上按照介绍应该是需要volatile关键字的 。。主线程 执行flag =TRUE之后,虽然执行完了 ,但是另外一个线程依然应该执行 ,因为他没有看到flag 的值的改变。还有5楼的, 你说是vm版本的问题 没看到 书上写到过。难道vm版本会让 java代码语义发生变化吗。那这样 对于java的到处运行不是起到影响了 ,,,为什么要有2个版本呢 。。他们的区别和意义在哪里呢?
      

  5.   

    您的那个例子来自是Effective Java的‘Synchronize access to shared mutable data’,我看到书里面也指出了这是个optimization是来自HotSpot Server。把代码优化成:
    if(!flag)
       while(true)
          i++;
    Bloch也只是说这是‘quite acceptable’:)
      

  6.   

    你加不加 这个关键字   这个线程都会关闭的
    这个例子说明了什么呢? 我到现在也不清楚volatile的意思是可见的  意思就是说一个线程改变了这个值  别的线程"立马"就能看的到      
    你不加这个关键字   那么一个线程改变了这个值  别的线程"不能立马"就能看的到  也有可能一秒或者几百年才看的到   也就是说第一个线程改变了值 另外一个线程读取到的可能还是旧的数据  
      

  7.   

    你为什么那么肯定主线程执行flag =TRUE之后,虽然执行完了 ,但是另外一个线程依然应该执行 ,因为他没有看到flag 的值的改变子线程看不到呀( ⊙ o ⊙ )?晕...因为子线程在不断循环判断flag是不是为false,子线程执行时怎么会看不到flag为true呢?( ⊙ o ⊙ )!
      

  8.   

    楼主说: 主线程 执行flag =TRUE之后,虽然执行完了 ,但是另外一个线程依然应该执行 ,因为他没有看到flag 的值的改变。我就想问他怎么会看不到?????????
    我的回答是 :  一定看的到新的值  只是看到这个新值的时间是1秒还是100年 因素有很多难道你在一边写完一个变量 再访问的时候难道永远看不到   那java完了  彻底完了  我不用编程了  我死了算了5楼说的研究过头了吧  这根VM有关系吗? 这又让我想死了
      

  9.   

    VM的版本的差异导致thread执行的不同不是我说的,是Joshua Bloch说的,我觉得他比我们都权威:)
      

  10.   

    纳闷,这跟那个VM版本有what关系( ⊙ o  ⊙ )?这个又不是bug...
      

  11.   

    楼上的几位兄弟说 ,一个线程改变了 另外一个线程 可以看到分别是 1秒或者100秒的事情 。但是这个程序我执行了很多次,基本上都是立刻看到的 。可能误差不过是几毫秒。子线程是在不停的判断 flag = 什么 但是 不是一个在同一个线程改变的他有可能看到 。这个不是一本书里面说的 ,core java 里面也有。还有有位兄弟说:“难道你在一边写完一个变量 再访问的时候难道永远看不到 那java完了 彻底完了 我不用编程了 我死了算了”一边写变量 另外访问可以看到 ,这个 是在一个线程内 理论上是这样的。 而且我们大多数都是一个线程内用到的。多线程的时候如果同步了,也是可以看到的 
    这里是指volatile 的用法 。。在core java里面 是这样说的。。volatile 关键字为实例域的同步访问提高了一种免锁机制。如果声明一个域为volatile 那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。(换句话是不是应该理解为 如果不用volatile 那么编译器就不知道他会被另一个线程并发更新,或者是不能立刻知道,否则用volatile 这个有什么必要。)
      

  12.   

    说那么多LZ还是不明白,你根本没好好看我给你在三楼的解释,这个问题暂时不谈volatile(实际当中这个关键字用的极少),不要什么都书上怎么说怎么说的那种背教条,你自己多测试测试不就明白了,你把Thread.sleep(1000);  改成Thread.sleep(600000); 运行看看数字绝对要打印6分钟左右,然后程序结束;还有你把Thread.sleep(1000);  这句代码去掉看看是不是什么也不会打印...怎么那么简单的东西你会那么坚决的认为flag=true不会被运行,I 服了 you!
      

  13.   

    楼主问的问题是不是
     这个flag加volatile   2个线程才能同步到新值
    不加volatile那就不应该同步到新值了    你要问的是不是这个???????如果是  你这里有2个线程同时访问同一个变量flag , 一个是修改  一个是读取
    你子线程先执行读取flag操作 一直读取一直打印    后来主线程flag=true设置了新值   那么子线程就应该获取到flag的新值true  只是时间问题(这个时间问题后面再说)
     你前面说的   一边写变量 另外访问可以看到 ,这个 是在一个线程内 理论上是这样的。 而且我们大多数都是一个线程内用到的。多线程的时候如果同步了,也是可以看到的 
       你意思就是说不同步了  那么就获取不到新值了吗?  那是错的 ,  你就算不同步  也能获取到新值   你2个线程访问的都是同一个flag   
    我转一段话给你
     Java的内存模型分为主存储区和工作存储区。主存储区保存了Java中所有的实例。也就是说,在我们使用new来建立一个对象后,这个对象及它内部的方法、变量等都保存在这一区域,在MyThread类中的n就保存在这个区域。主存储区可以被所有线程共享。而工作存储区就是我们前面所讲的线程栈,在这个区域里保存了在run方法以及run方法所调用的方法中定义的变量,也就是方法变量。在线程要修改主存储区中的变量时,并不是直接修改这些变量,而是将它们先复制到当前线程的工作存储区,在修改完后,再将这个变量值覆盖主存储区的相应的变量值。----这里就有时间差异了,你这个例子这个时间差异是很小  你另外写一个例子建立几百个线程应该可以看的到时间差异了加volatile关键字使用同步的意思是什么呢?  就是为了让你在1个线程改变了值   其他线程获取到当前变量的新值的时候是"立马"获取到    同理 你自己去加同步锁也是一样的  也就是说让这2个线程共享同一个内存视图 一端改变 另外一端"立马"获取到
      

  14.   

    再摘取一段话给你   Volatile的实现同步的原理 :
     Volatile 比同步更简单,只适合于控制对基本变量(整数、布尔变量等)的单个实例的访问。当一个变量被声明成 volatile,任何对该变量的写操作都会绕过高速缓存,直接写入主内存,而任何对该变量的读取也都绕过高速缓存,直接取自主内存。这表示所有线程在任何时候看到的 volatile 变量值都相同。
      

  15.   

    我在HotSpot VM server上面运行 ,结果也是一样 的 
    我看了一下java的内存模型 ,  应该是线程运行的时候 会清空线程内存 ,把主内存最新的数据读取出来。。那么如果是主要 ,子线程就应该能读到 flag的值。 只是可能不是立刻而已 。但对于这个程序没有影响。感谢楼上几位兄弟的解释  这里我只是疑问Volatile  这个修饰的作用而已这样看来 Volatile 这个经典的例子事实上没什么意义了 。就是在dcl里面 在java5之后用他可以避免出错了。关键是effective java 和 java core 两本权威的书的解释Volatile 这样看来都是错误的 
    特别是effective java 里面写到 不用Volatile  ,他的机器将会永远执行下去 。就是上面的这个例子我一直不理解呀 ,难道真的是 他搞错了 ,他既然说他的机器运行下去,难道没测试过,为什么他测试 就会这样呢??
      

  16.   

    LZ相信自己的眼睛,这个程序绝对不会一直运行下去,有可能是书印刷出错了O(∩_∩)O~
      

  17.   

    你的代码,虽然是一个现成读,一个现程写,但是并没有对线程冲突那么敏感。volatile 保证了在写入后,或者写入中的第一时间你就能取到最新值,因为它是另外一种形式的同步。但是并不是说,你不加volatile ,java就不会把它更新成最新值。只是一个时间的问题,而不是结果的问题。结果是你总能取到最新值