搜尋

首頁  >  問答  >  主體

多執行緒 - 關於Java記憶體可見性的問題

請看以下程式碼

public class TestVolatile {
    
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo td = new ThreadDemo();
        new Thread(td).start();
        
        Thread.sleep(1);
        while(true){
            if(td.isFlag()){
                System.out.println("------------------");
                break;
            }
        }
        
    }

}

class ThreadDemo implements Runnable {

    private boolean flag = false;

    @Override
    public void run() {
        
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
        }

        flag = true;
        
        System.out.println("flag=" + isFlag());

    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

}

Thread.sleep(1)換成Thread.sleep(1000)就能取得flag修改後的值,即td .isFlag()傳回true
雖然看了Java記憶體模型的概念,但我不知道該如何解釋這段程式碼,誰能解釋一下?

相關問題: Java多執行緒的工作記憶體是什麼?

漂亮男人漂亮男人2775 天前966

全部回覆(4)我來回復

  • 淡淡烟草味

    淡淡烟草味2017-05-17 10:08:58

    你得先說說你的預期效果是啥?問問題想問清楚

    回覆
    0
  • 滿天的星座

    滿天的星座2017-05-17 10:08:58

    這個期待是沒有規範支撐的。程式碼中沒有做任何能保證 "子執行緒寫 happen-before 主執行緒讀" 的事情。

    sleep(1000)後看到修改只是巧合,一個JVM如果在更久後才讓主線程看到,甚至永遠不讓主線程看到都不違反規範。

    回覆
    0
  • 怪我咯

    怪我咯2017-05-17 10:08:58

    你的程式應該是想測試 volatile 关键字的功能。但是 “把 Thread.sleep(1) 换成 Thread.sleep(1000) 就能获得预期效果” 这样做理解上是不对的。
    首先,程序中总共有两个线程,主线程(暂称 线程M)和 new Thread(td) (暫稱 線程T)。


    當寫 Thread.sleep(1) 的时候,线程M 在 1ms 之后,便开始在 while(true) 循环中检查 td.isFlag() 的值,但是因為記憶體可見性的關係,線程M 並不能及時讀取 線程T 中 flag 的值,所以此時導致了死循環;


    當寫Thread.sleep(1000) 的時候,M 在1000ms 之後,開始在while(true) 循環中檢查td.isFlag() 的值;但是T 在200ms 的時候,便將Thread.sleep(1000) 的时候,M 在 1000ms 之后,开始在 while(true) 循环中检查 td.isFlag() 的值;但是 T 在 200ms 的时候,便将 flag 的值设为 true 了,所以,M 在 1000ms 之后检测 td.isFlag() 的值肯定是返回 true 的,那么第一次判断便会返回 true,产生输出并跳出 while(true)flag

    的值設為true 了,所以,M 在1000ms 之後檢測td.isFlag()的值肯定是回傳true 的,那麼第一次判斷便會回傳true,產生輸出並跳出while(true) 迴圈。

    為了讓 執行緒M 及時讀取到 執行緒T 中 flag 的值,需要將 flagvolatile 使用

    關鍵字進行修飾:

    private volatile boolean flag = false;
    那麼每次對 flagvolatile 的修改,其他線程都立刻可見。關於

    的使用,可以參考我的部落格:Java 多執行緒(6):volatile 關鍵字的使用🎜

    回覆
    0
  • 大家讲道理

    大家讲道理2017-05-17 10:08:58

    可以參考以下三個程式碼:
    其中第一個和你的情況一樣,由於多執行緒的可見性問題,可能導致無限循環下去。
    第二個是使用synchronized解決此問題,大多數工作場景用這個好synchronized解决此问题,大多数工作场景用这个好
    第三个是使用volatile第三個是使用volatile解決,但這個關鍵字只保證可見性,在實際場景中限制比較大,得慎用

    public class StopThread {
        
        private static boolean stopRequested;
        
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    @SuppressWarnings("unused")
                    int i = 0;
                    while(!stopRequested) {
    //                    System.out.println("加上这一句程序就可以终止,否则无限循环下去");
                        i++;
                    }
                }
            });
            
            backgroundThread.start();
            TimeUnit.SECONDS.sleep(1);
            stopRequested = true;
        }
    }
    public class StopThread2 {
        
        private static boolean stopRequested;
        
        public static synchronized boolean getStopRequested() {
            return stopRequested;
        }
        
        public static synchronized void requestStop() {
            stopRequested = true;
        }
        
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    @SuppressWarnings("unused")
                    int i = 0;
                    while(!getStopRequested()/* stopRequested */) {
                        i++;
                    }
                }
            });
            
            backgroundThread.start();
            TimeUnit.SECONDS.sleep(1);
            requestStop();/* stopRequested = true; */
        }
    }
    public class StopThread3 {
        
        private static volatile boolean stopRequested;
        
        
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    @SuppressWarnings("unused")
                    int i = 0;
                    while(stopRequested) {
                        i++;
                    }
                }
            });
            
            backgroundThread.start();
            TimeUnit.SECONDS.sleep(1);
            stopRequested = true;
        }
    }

    回覆
    0
  • 取消回覆