java 多線程中的一個奇怪的地方,希望有識之人幫助解答疑惑

碼上中國博客 發布于 02/14 09:25
閱讀 267
收藏 0

簡單一個例子如下:

public class VolatileTest {
	public static void main(String[] args) throws InterruptedException {
		MyThread1 myThread1 = new MyThread1();
		myThread1.start();
		Thread.sleep(3000);
		myThread1.setFlag(false);
		Thread.sleep(1000);
		System.out.println("flag:"+myThread1.flag);
		System.out.println("結束");
	}
}
class MyThread1 extends Thread{
	
	public boolean flag = true;
	
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	
	@Override
	public void run() {
		System.out.println("線程開始>>>>>>>>>>>>>");
		while (flag) {
//			System.out.println("運行中,子線程:"+Thread.currentThread().getName());
		}
		System.out.println("線程結束");
	}
	
}

在實際運行中,可以嘗試分別注釋和不注釋下面的代碼

System.out.println("運行中,子線程:"+Thread.currentThread().getName());

會產生不同的效果。

例如,不注釋的話,線程會打印上面的日志,直到整個線程終結,eclipse的控制臺的運行標志也會變灰(這是重點)

但是如果注釋掉上面這行代碼,則eclipse的控制臺運行標志會一直為運行狀態紅色(運行中)

初學多線程,請有識之士幫助解決下這個原理是什么。

加載中
0
tcxu
tcxu

樓主的案例有二。
   案例1:注釋掉這行代碼,即便驅動方法 main 線程也終止,線程 Thread-0 仍然保持運行, 所以 eclipse 的控制臺運行標志為紅色,表示還有程序正在運行。

   案例2:不注釋的話,即 由于 線程 Thread-0 的 while 循環體的代碼調用 System.out.println(...) 打印出信息,而終止了該線程。之后,驅動方法 main 線程也終止,致使 eclipse的控制臺的運行標志顯示變灰: 無程序運行。

   案例1的解釋:
"Java內存模型分為主內存,和工作內存。主內存是所有的線程所共享的,工作內存是每個線程自己有一個,不是共享的"。下圖表明線程、主內存、工作內存三者之間的交互關系:


每個線程都有自己的工作內存,工作內存中保存了被該線程使用到的變量的主內存副本。線程對變量的所有操作(讀取、賦值),都是在工作內存中進行,而不是直接讀寫主內存中的變量,即談不上內存間的"可見性"。線程之間,無法直接訪問對方工作內存中的變量,而線程間變量值的傳遞均需通過主內存來完成。
因此,線程 Thread-0 的變量 flag 在其工作內存的值始終為 true。而主方法 main 線程,執行 myThread1.setFlag(false) 操作,只是將主內存里的 flag 變為 false,而 Thread-0 線程的工作內存里的變量 flag,仍然保持為 true 不變。故 Thread-0 線程將永遠運行,致使 eclipse 的控制臺運行標志永遠顯示紅色。 

   案例2的解釋:
此時,while 循環體內,添加了 System.out.println(...)操作。由于 println 內部代碼(源碼)的同步 synchronized 關鍵字,導致 CPU 的輸出比較耗時。這時 CPU 就有可能、有時間去保證內存的可見性,即統一了主內存和工作內存的變量 flag 的 false 值,于是 while 循環就被終止,即 線程 Thread-0 終止。

     若將 類 MyThread1 的公有屬性(成員變量 flag)冠以關鍵字 volatile, 就能強制地保證線程內存的可見性。這樣,注釋 還是不注釋  System.out.println(...),就沒有區別了:最終都會終止 線程 Thread-0 的運行,控制臺終顯灰色。

參考:

  1. 多線程:為什么在while循環中加入System.out.println,線程可以停止
  2. Java---線程多(工作內存)和內存模型(主內存)分析
  3. Java線程工作內存與主內存變量交換過程及volatile關鍵字理解
0
卻又讓幽蘭枯萎
卻又讓幽蘭枯萎

 

private boolean getFlag(){

    return flag;

}

public void run() {

            System.out.println("線程開始>>>>>>>>>>>>>");

            while (getFlag()) {

                    // 這里休眠一下也可以:Thread.sleep(100);

                    // System.out.println("運行中,子線程:"+Thread.currentThread().getName());

            }                

         System.out.println("線程結束");

}

卻又讓幽蘭枯萎
卻又讓幽蘭枯萎
這個問題和編譯器有關,要真正理解問題的原因需要一些匯編的知識
返回頂部
頂部
真人龙虎斗论坛