CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
可用分押宝游戏火热进行中... 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  Java >  J2SE / 基础类

一段怪异的Java小程序

楼主darksideofjava(秦王骑虎)2006-12-03 14:35:59 在 Java / J2SE / 基础类 提问

新浪的Java圈http://circle.blog.sina.com.cn/g/javaworld中有一段Java小程序  
   
  http://blog.sina.com.cn/u/4ad4a32e0100062i  
   
  怎么解释它呀? 问题点数:100、回复次数:32Top

1 楼darksideofjava(秦王骑虎)回复于 2006-12-04 18:49:26 得分 0

加到100分,有人会吗?Top

2 楼zzwh_0(混这么长时间了怎么还俩裤衩)回复于 2006-12-04 19:32:02 得分 0

我帮你贴出来吧,看看执行结果  
  class   Init{  
                  static   volatile   boolean   done   =   false;  
                  static   {  
                                  new   Thread()   {  
                                                  public   void   run()   {  
                                                                  System.out.println("enter   thread");  
                                                                  done   =   true;  
                                                  }  
                                  }.start();  
   
                                  while(!done);  
                  }  
                  public   static   void   main(String[]   args)    
                  {  
                  }  
  }Top

3 楼jlu3389(激情的89)回复于 2006-12-04 19:34:54 得分 0

class   Init{  
                  static   volatile   boolean   done   =   false;  
                  static   {  
                                  new   Thread()   {  
                                                  public   void   run()   {  
                                                                  System.out.println("enter   thread");  
                                                                  done   =   true;  
                                                  }  
                                  }.start();  
                                   
                                  while(!done){};  
                  }  
                  public   static   void   main(String[]   args)   {}  
  }  
   
  ===============================================================  
  我对这个程序分析是这样:  
  会打印一个enter   thread,然后就死循环了,为什么会这样,就是因为volatile关键字。根据程序初始化顺序,会执行到new   Thread(){……},然后创建一个线程并执行,但这并不阻塞主线程,它会继续执行到while   (!done),而这个时候done还是false,程序开始死循环,这个时候创建的线程已经启动并开始执行,从死循环中挤出很少的资源打印出了enter   thread,然后准备done   =   true,可发现不行,因为done是被volatile修饰的,所以就要等主线程结束,死锁了。Top

4 楼darksideofjava(秦王骑虎)回复于 2006-12-04 20:40:29 得分 0

为什么将volatile关键字去掉也不行呢?Top

5 楼jlu3389(激情的89)回复于 2006-12-04 21:47:56 得分 0

线程为了速度,是允许获得一个变量的副本,也就是static{}里的done和while里的done跟本不是一个。  
  其实线程是可以被结束的,但while的优先级太高,以至于线称被饿死了,所以去掉volatile也一样,而且volatile对boolean是无效的。Top

6 楼jlu3389(激情的89)回复于 2006-12-04 21:57:08 得分 0

我收回   刚才的回答,支持第一次的回答。Top

7 楼darksideofjava(秦王骑虎)回复于 2006-12-04 22:06:33 得分 0

为什么在while中加入sleep或wait都不行呢?这时Task   Manager显示这个进程只占用很少的CPU。而且将那个线程的优先级设成最大也无济于事。Top

8 楼lbfhappy(千里冰封)回复于 2006-12-04 22:08:16 得分 0

其实根本不是volatile有关系,volatile只是表示它的值是易变的,每个线程不能保存自己的变量考贝,而是每次要取的时候重新去取.  
  我试了一下这个程序,done=true;这句话之所以不能执行,是因为JAVA在静态变量初始化之前是不允许去访问静态变量的  
  也就是说所有的static变量或者static都执行完了以后,才可以访问static变量,因为static初始化块由于有一个while(!done)死循环在那里,所以它永远不会返回,所以起的那个线程就永远访问不到static的变量done了,因为JVM认为此时done可能还没有初始化完全.  
  我自己加了一些东西,希望可以让大家了解得更清楚,  
  你们可以注意一下打印出的顺序  
  ===============================================================================  
  /*  
    *   Init.java  
    *  
    *   Created   on   2006年12月4日,   下午9:45  
    *  
    *   To   change   this   template,   choose   Tools   |   Template   Manager  
    *   and   open   the   template   in   the   editor.  
    */  
   
  package   test1;  
   
  /**  
    *  
    *   @author   lbf  
    */  
  class   Init{  
                  static     boolean   done   =   false;  
                  static   int   i=0;  
                  static   {  
                                  new   Thread()   {  
                                                  public   void   run()   {  
                                                                  System.out.println("enter   thread");//这句会执行  
                                                                  System.out.println("hello");//这句会执行  
                                                              System.out.println("done="+done);//这句会访问到可能没有初始化的变量,所以未初始化完成后,不会执行  
                                                  }  
                                  }.start();  
                                  int   all=0;  
                                  while(!done){  
                                          try{  
                                                  Thread.sleep(100);  
                                                  if(all++>=10){  
                                                          done=true;  
                                                  }  
                                          }  
                                          catch(Exception   exe){  
                                                  exe.printStackTrace();  
                                          }  
                                  }  
                                  System.out.println("left   while");//离开了一个static初始化块  
                  }  
                  static{  
                          try{  
                                  Thread.sleep(1000);  
                                  System.out.println("done   the   1S");//这句话打印出以后过一秒钟离开这个初始化块,然后线程里面的那句话才执行了  
                                  Thread.sleep(1000);  
                          }  
                          catch(Exception   exe){  
                                  exe.printStackTrace();  
                          }  
                  }  
                  public   static   void   main(String[]   args)   throws   Exception{  
                  }  
  }Top

9 楼lbfhappy(千里冰封)回复于 2006-12-04 22:09:48 得分 0

to   jlu3389(激情的89)    
  你的解释是不对的  
  volatile一般就是用于修饰boolean的,为了保证多线程访问同一变量的一致性Top

10 楼darksideofjava(秦王骑虎)回复于 2006-12-04 22:20:45 得分 0

done在进入static块之前就被初始化了。可以在刚进入static块时打印出done的值。这个程序怪就怪在主线程(执行static块的那个线程)中可以自由访问变量,但其他线程不行。Top

11 楼lbfhappy(千里冰封)回复于 2006-12-04 22:37:22 得分 0

在static块里面随时可以访问static   变量,因为JVM会认为这是在初始化阶段,是在给static变量赋值  
   
  而别的线程要访问肯定不行,因为static块执行完之前,static的值都是不确定的,所以在别的线程要访问必须等到static全部初始化以后Top

12 楼darksideofjava(秦王骑虎)回复于 2006-12-04 22:58:32 得分 0

lbfhappy说的有道理。为了进一步验证,我将原始程序稍加修改如下。按说虽然不能访问done,至少可打印出"enter   foo",为什么这一条语句也不能执行呢?  
   
  class   Init{  
   
  static   volatile   boolean   done   =   false;  
   
  static   void   foo()   {  
                  System.out.println("enter   foo");  
                  done   =   true;  
  }  
   
  static   {  
  new   Thread()   {  
  public   void   run()   {  
  System.out.println("enter   thread");  
  foo();  
  }  
  }.start();  
   
  while(!done);  
  }  
  public   static   void   main(String[]   args)   {}  
   
  }Top

13 楼lbfhappy(千里冰封)回复于 2006-12-04 23:07:58 得分 0

在初始化的阶段,是不允许调用任何本类定义的方法和变量的  
   
  因为static   方法可能会访问static   的变量,也许这个时候它所需要访问的变量还没有被初始化  
  Top

14 楼lbfhappy(千里冰封)回复于 2006-12-04 23:11:31 得分 100

在JAVA类没有完全初始化之前,是不允许除初始化之外的别的线程调用它的方法或者变量的  
  以免出初化出错  
   
  我们现在只能这样理解了Top

15 楼darksideofjava(秦王骑虎)回复于 2006-12-04 23:59:40 得分 0

同意lbfhappy。接100分.Top

16 楼sonyejin(Neverwinter Nights 2)回复于 2006-12-05 11:29:47 得分 0

哦,遇到高手啦  
  但我还是对volatile关键字搞不清,不知道有什么用Top

17 楼lavendergo( 王者归来)回复于 2006-12-05 12:18:09 得分 0

markTop

18 楼piaopiao11()回复于 2006-12-05 12:19:18 得分 0

这样也要等10秒,跟while无关  
  class   Init{  
          static   volatile   boolean   done   =   false;  
          static   {  
                new   Thread()   {  
                          public   void   run()   {  
                                          System.out.println("enter   thread");  
                                          done   =   true;  
                                          System.out.println(done);  
                          }  
                        }.start();  
                    try   {  
                          Thread.sleep(10000);  
                  }   catch   (InterruptedException   e)   {  
                          e.printStackTrace();  
                  }    
                  System.out.println(done);  
                    //   while(!done);  
                         
          }  
          public   static   void   main(String[]   args)    
          {  
          }  
  }Top

19 楼by827(白杨)回复于 2006-12-05 14:45:25 得分 0

我稍改一点程序:  
  public   class   Init   {  
          static       boolean   done=false;  
   
          static   {  
                  new   Thread()   {  
                                  public   void   run()   {  
                                          System.out.println("enter   thread");  
                                          done   =   false;  
                                  }  
                          }.start();  
   
                  if   (!done)   {  
                  System.out.println("test");  
                  };  
          }  
   
          public   static   void   main(String[]   args)   {  
          }  
  }  
  运行结果:  
  test  
  enter   thread  
   
  这里有两个线程,一个是主线程,一个是自定义的线程,  
  当程序初始化的时候,主线程初始化static变量和static块,同时在主线程里产生了一个自定义线程,  
  从运行结果可以看出,程序是先初始化全部完成后才运行自定义线程.自定义线程延时启动才产生了这样的结果.  
  Top

20 楼fengbo81()回复于 2006-12-05 14:52:50 得分 0

lbfhappy(千里冰封)   所做的解释应该差不多了,但我觉得还有一些不太准确的地方  
   
  首先,大家都知道静态块是在类加载时执行的  
   
  done=true;这句话之所以不能执行,不是因为JAVA在静态变量初始化之前是不允许去访问静态变量的,而是因为Init类在初始化完成之前是不允许访问的。源代码中的done=true如果写成Init.done=true相信就更容易理解了,下面结合代码做一下解释  
   
  class   Init{  
  static   volatile   boolean   done   =   false;  
          static   {  
                          new   Thread()   {  
                                          public   void   run()   {  
                                                          System.out.println("enter   thread");   //此句会被打印,说明此现程有机会获得cpu  
                                                          System.out.println("done="+done);   //此句相当于System.out.println("done="+Init.done);   因为后面的while(!done)循环结束之前Init类病没完成初始化,所以不能访问  
                                                          done   =   true;//此句相当于done   =   Init.done;                                                      
                                          }  
                          }.start();  
   
                          while(!done){  
                                  System.out.println("in   while   done="+done);     //此句被循环打印,说明在此静态块中,静态变量done是可以访问到的,也就是说静态变量会在静态块执行之前初始化.但是此时并没有完成类Init的初始化,所以Init类是不能访问的,所以启动的另外的线程此时想访问Init.done是访问不到的,所以此时done的值,在本初始化线程之外是永远不能被改变的,所以永远驯化下去  
                                  try{  
                                          Thread.sleep(1000);  
                                  }catch(Exception   e){}  
                          };  
          }  
                  public   static   void   main(String[]   args)   {}  
  }Top

21 楼by827(白杨)回复于 2006-12-05 14:58:48 得分 0

所以,楼主的程序会出现死循环,跟volatile无关Top

22 楼laiwusheng(风清扬)回复于 2006-12-05 17:11:44 得分 0

markTop

23 楼jameswu525()回复于 2006-12-05 18:20:16 得分 0

fengbo81()   ,最终   结果。。。。  
   
  研究了一个下午,终于明白了。!~  
   
  原来此处的volatile只是一个障眼关键字,实际上就是加载类的时候(首先要处理Static部分),该类的成员外部都是无法访问的,自定义线程当然也就无法访问,只能挂起,所以对done的修改也就不能执行!~  
   
   
  下面的一段程序很清楚  
   
   
  package   src;  
   
  public   class   VolatileTest2   {  
  public   static   void   main(String[]   args)   {  
  Test   test   =   new   Test();  
  test.excute();  
  }  
  }  
   
   
  class   Test   {  
   
  static   volatile   boolean   done   =   false;  
   
  /**  
    *   @param   args  
    */  
  public   void   excute()   {  
  new   Thread()   {  
  public   void   run()   {  
  System.out.println("Enter   thread   A");  
  done   =   true;  
  System.out.println("Exit   thread   A");  
  }  
  }.start();  
   
  System.out.println("Enter   thread   MAIN");  
  while   (!done)   {  
  }  
  System.out.println("Exit   thread   MAIN");  
  }  
   
  }  
   
   
  成功执行,结果如下:  
  Enter   thread   MAIN  
  Enter   thread   A  
  Exit   thread   A  
  Exit   thread   MAIN  
   
   
   
   
  稍加修改之后如下,添加  
  (static   {while(!done)   {}}):  
   
   
  package   src;  
   
  public   class   VolatileTest2   {  
  public   static   void   main(String[]   args)   {  
  Test   test   =   new   Test();  
  test.excute();  
  }  
  }  
   
   
  class   Test   {  
   
  static   volatile   boolean   done   =   false;  
   
  static   {while(!done)   {}}  
   
  /**  
    *   @param   args  
    */  
  public   void   excute()   {  
  new   Thread()   {  
  public   void   run()   {  
  System.out.println("Enter   thread   A");  
  done   =   true;  
  System.out.println("Exit   thread   A");  
  }  
  }.start();  
   
  System.out.println("Enter   thread   MAIN");  
  while   (!done)   {  
  }  
  System.out.println("Exit   thread   MAIN");  
  }  
  }  
   
  这样就无法执行下去,只能被死循环。Top

24 楼cai_yang(不帅不要钱)回复于 2006-12-05 18:25:06 得分 0

markTop

25 楼galahadzzy(圣冰之火)回复于 2006-12-05 20:33:12 得分 0

markTop

26 楼kava_java()回复于 2006-12-06 01:43:00 得分 0

fengbo81()   的解释,好像更容易理解,呵呵,学习Top

27 楼horsoner(Kity)回复于 2006-12-06 08:11:50 得分 0

markTop

28 楼bl_song()回复于 2006-12-06 10:06:46 得分 0

学习Top

29 楼tony_yc()回复于 2006-12-06 10:22:54 得分 0

学习了,谢谢Top

30 楼mr_liyf()回复于 2006-12-06 11:48:21 得分 0

哈哈,我也明白了。  
  所有的static变量和块,等主线程初始化完成后,其它线程才能访问主线程里需初始化的(static)变量,包括块里启动的线程。  
  while(!done);  
  是主线程里的语句,所以能访问前面已初始化的变量,而  
  new   Thread()   {  
                                          public   void   run()   {  
                                                          System.out.println("enter   thread");  
                                                          done   =   true;  
                                          }  
                          }.start();  
  是块里启动的线程,属其它线程。因为while(!done)已死循环,块就永远不能执行完,并且还影响其它线程来访问主线程里已初始化的变量。Top

31 楼zhengjianqi(zzz)回复于 2006-12-06 14:16:49 得分 0

学习中Top

32 楼appleheno(游鱼)回复于 2006-12-06 22:20:47 得分 0

markTop

相关问题

关键词

得分解答快速导航

  • 帖主:darksideofjava
  • lbfhappy

相关链接

  • CSDN Java频道
  • Java类图书
  • Java类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo