前言:本文已在論壇發起提問,如有滿意答案,會繼續寫在這裏,同時也請看過本文的大牛留下寶貴意見!
筆者是一個普通大學大二非計算機專業的學生,在學習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 } |