一段怪异的Java小程序
新浪的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




