1. 程式人生 > 實用技巧 >多執行緒的安全問題

多執行緒的安全問題

一、可見性

  • 例如下面的程式,先啟動一個執行緒,線上程中將一個變數的值更改,而主執行緒卻一直無法獲得此變數的新值。
    //1.執行緒類
            public class MyThread extends Thread {
                public static int a = 0;
                @Override
                public void run() {
                    System.out.println("執行緒啟動,休息2秒...");
                    try {
                        Thread.sleep(
    1000 * 2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("將a的值改為1"); a = 1; System.out.println("執行緒結束..."); } }
    //測試類
    public class Demo {
    public static void main(String[] args) {
    MyThread t = new MyThread();
    t.start();
       //主執行緒繼續
    while (true) {
    if (MyThread.a == 1) {
    System.out.println("主執行緒讀到了a = 1");
    }
    }
    }
    }

二、有序性

  • 有些時候“編譯器”在編譯程式碼時,會對程式碼進行“重排”,例如:
int a = 10; //1 int b = 20; //2 int c = a + b; //3 第一行和第二行可能會被“重排”:可能先編譯第二行,再編譯第一行,總之在執行第三行之前,會 將1,2編譯完畢。1和2先編譯誰,不影響第三行的結果。
  • 但在“多執行緒”情況下,程式碼重排,可能會對另一個執行緒訪問的結果產生影響:

三、原子性

  • 請看以下示例
//執行緒類
public class MyThread extends Thread {
    public static int a = 0;
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            a++;
        }
        System.out.println("修改完畢!");
    }
}
//測試類
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start();
        t2.start();
        Thread.sleep(1000);
        //總是不準確的。原因:兩個 執行緒訪問a的步驟不具有:原子性
        System.out.println("獲取a最終值:" + MyThread.a);
    }
}
  • 記憶體工作圖

  

執行緒t1先讀取a 的值為:0

t1被暫停

執行緒t2讀取a的值為:0 t2將a = 1 t2將a寫回主記憶體 t1將a = 1 t1將a寫回主記憶體(將t2更改的1,又更改為1) 所以兩次加1,但結果仍為1,少加了一次。 原因:兩個執行緒訪問同一個變數a的程式碼不具有"原子性"