前言:本文已在论坛发起提问,如有满意答案,会继续写在这里,同时也请看过本文的大牛留下宝贵意见!
笔者是一个普通大学大二非计算机专业的学生,在学习java多线程问题的时候出于某种原因碰见了一个难以解决的问题。之所以说是难以解决,是因为它既不是逻辑问题,也不是语法问题。笔者对java的认识并不深刻,特此将这个问题拿上来同各路大牛分享,希望能得到一个满意的答复,先谢过各路大牛~
笔者有长时间在贴吧潜水的经历,懂得提问的规矩,为了帮助大家理解我遇到的问题,我特地写了测试程序(如下),并把我的一些挣扎和思考的过程意义列举出来。测试程序如下:
运行环境:JDK1.6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package TestThread; public class TestThread { //定义Flag,Flag = true,分线程空转,Flag = false,分线程执行输出 private static boolean Flag = false ; //分线程 class MyThread implements Runnable{ @Override public void run(){ while ( true ){ System.out.println( "分线程debug语句" ); while (!Flag){ for ( int i = 0 ;i < 200 ;i++) System.out.println( "分线程输出" ); } } } } //主线程 public static void main(String[] args){ TestThread tt = new TestThread(); //启动分线程 MyThread mt = tt. new MyThread(); Thread t = new Thread(mt); t.start(); //主线程sleep3s System.out.println( "让分线程输出3s" ); try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "让分线程空转3s" ); Flag = true ; //主线程sleep3s try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "让分线程输出" ); Flag = false ; System.out.println(t.isAlive()); } } |
测试程序描述的是两个线程并发的过程,思路比较明确,不多赘述。笔者通过主线程给分线程传递不同的信号量(Flag = true or false),使分线程在Flag = true时什么也不做,输出debug语句,在Flag = false时,输出内层while循环的语句。
主线程执行如下功能:1、激活分线程,Flag = false;2、三秒之后,Flag = true;3、三秒之后,Flag = false。得到的运行结果和预期一样:分线程先输出内层循环语句,三秒空转,然后再输出内层循环语句。
然后问题来了。笔者手贱,将System.out.println("分线程debug语句");一行给注释掉了。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package TestThread; public class TestThread { //定义Flag,Flag = true,分线程空转,Flag = false,分线程执行输出 private static boolean Flag = false ; //分线程 class MyThread implements Runnable{ @Override public void run(){ while ( true ){ //System.out.println("分线程debug语句"); while (!Flag){ for ( int i = 0 ;i < 200 ;i++) System.out.println( "分线程输出" ); } } } } //主线程 public static void main(String[] args){ TestThread tt = new TestThread(); //启动分线程 MyThread mt = tt. new MyThread(); Thread t = new Thread(mt); t.start(); //主线程sleep3s System.out.println( "让分线程输出3s" ); try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "让分线程空转3s" ); Flag = true ; //主线程sleep3s try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "让分线程输出" ); Flag = false ; System.out.println(t.isAlive()); } } |
然后笔者得到的执行结果是:三秒分线程先输出内层循环语句,三秒空转,然后再输出内层循环语句。当主线程再将Flag = false时,分线程却没有如期地再次输出内层循环(笔者亲验)。
笔者不相信自己的眼睛,于是调用了isAlive函数去看看分线程是否还活着,结果返回true,分线程确实还存在,但是却不工作了。笔者才疏学浅,无法解释这一现象,但是在和伙伴的交流讨论中,得出了如下两种推论。这两种可能有一种是正确的,也有可能都是错的,现在都列出来,请各路大牛批评指正:
1、分线程内循环在某个时刻接受到true(不工作)时,java虚拟机认为该线程做的是无用功,虽然分线程还活着,但是不再为其分配CPU,即使是分线程后来又可以正常工作了。
2、分线程内循环在某个时刻接受到true(不工作)时,java虚拟机认为该线程做的是无用功,自己给代码做了优化,莫名其妙就变成这个样子了。
第二种观点是我的小伙伴提出来的。刚开始提出来的时候我认为他说的跟我第一种的想法其实是一个意思,他说不一样。他认为我的想法(第一种)是在运行时发生的,而他的想法是代码在编译阶段已经产生了分歧,即两段代码虽然只是一句System.out之差,编译的结果却是大不相同的。为了说服我,他(大牛)给我导出了两段代码各自的字节码作比较。然而我并不能看懂……我也不知道如何导出字节码,所以字节码就不上代码了,感兴趣的大牛辛苦一下哈,可以自己试试导出看看……
以上就是我遇到的问题。这是笔者提问的处女贴,希望各路大牛不吝赐教,在此先谢过了!
补充两段程序的字节码:
有System.out.println("分线程debug语句");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
Compiled from "TestThread.java" class TestThread.TestThread$MyThread extends java.lang.Object implements java.lang.Runnable{ final TestThread.TestThread this$0; TestThread.TestThread$MyThread(TestThread.TestThread); Code: 0:
aload_0 1:
aload_1 2:
putfield #12; //Field this$0:LTestThread/TestThread; 5:
aload_0 6:
invokespecial #14; //Method java/lang/Object."<init>":()V 9:
return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this LTestThread/TestThread$MyThread; public void run(); Code: 0:
getstatic #22; //Field java/lang/System.out:Ljava/io/PrintStream; 3:
ldc #28; //String 分线程debug语句 5:
invokevirtual #30; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8:
goto 34 11:
iconst_0 12:
istore_1 13:
goto 27 16:
getstatic #22; //Field java/lang/System.out:Ljava/io/PrintStream; 19:
ldc #36; //String 分线程输出 21:
invokevirtual #30; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 24:
iinc 1, 1 27:
iload_1 28:
sipush 200 31:
if_icmplt 16 34:
invokestatic #38; //Method TestThread/TestThread.access$0:()Z 37:
ifeq 11 40:
goto 0 LineNumberTable: line 12: 0 line 13: 8 line 14: 11 line 15: 16 line 14: 24 line 13: 34 line 11: 40 LocalVariableTable: Start Length Slot Name Signature 0 43 0 this LTestThread/TestThread$MyThread; 13 21 1 i I } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
无System.out.println("分线程debug语句"); Compiled from "TestThread.java" class TestThread.TestThread$MyThread extends java.lang.Object implements java.lang.Runnable{ final TestThread.TestThread this$0; TestThread.TestThread$MyThread(TestThread.TestThread); Code: 0:
aload_0 1:
aload_1 2:
putfield #12; //Field this$0:LTestThread/TestThread; 5:
aload_0 6:
invokespecial #14; //Method java/lang/Object."<init>":()V 9:
return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this LTestThread/TestThread$MyThread; public void run(); Code: 0:
goto 26 3:
iconst_0 4:
istore_1 5:
goto 19 8:
getstatic #22; //Field java/lang/System.out:Ljava/io/PrintStream; 11:
ldc #28; //String 分线程输出 13:
invokevirtual #30; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 16:
iinc 1, 1 19:
iload_1 20:
sipush 200 23:
if_icmplt 8 26:
invokestatic #36; //Method TestThread/TestThread.access$0:()Z 29:
ifeq 3 32:
goto 26 LineNumberTable: line 13: 0 line 14: 3 line 15: 8 line 14: 16 line 13: 26 line 11: 32 LocalVariableTable: Start Length Slot Name Signature 0 35 0 this LTestThread/TestThread$MyThread; 5 21 1 i I } |