1. 程式人生 > >JVM十二:重排序

JVM十二:重排序

資料依賴性含義

     如果兩個操作訪問同一個變數,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在資料依賴性。資料依賴分為下列3種類型,如表3-4所示。

上面3種情況,只要重排序兩個操作的執行順序,程式的執行結果就會被改變。

前面提到過,編譯器和處理器可能會對操作做重排序。編譯器和處理器在重排序時,會遵守資料依賴性,編譯器和處理器不會改變存在資料依賴關係的兩個操作的執行順序。

這裡所說的資料依賴性僅針對單個處理器中執行的指令序列和單個執行緒中執行的操作,不同處理器之間和不同執行緒之間的資料依賴性不被編譯器和處理器考慮。

什麼是重排序問題

    Java記憶體模型中,允許編譯器和處理器對指令進行重排序,但是重排序可以保證最終執行的結果是與程式順序執行的結果一致,並且只會對不存在資料依賴性的指令進行重排序,這個重排序在單執行緒下對最終執行結果是沒有影響的,但是在多執行緒下就會存在問題。如下例子

int a = 1;(1)

int b = 2;(2)

int c= a + b;(3)

      如上c的值依賴a和b的值,所以重排序後能夠保證(3)的操作在(2)(1)之後,但是(1)(2)誰先執行就不一定了,這在單執行緒下不會存在問題,因為並不影響最終結果。一個多執行緒例子

public static class ReadThread extends Thread {
        public void run() {
            
            while(!Thread.currentThread().isInterrupted()){
                if(ready){(1)
                    System.out.println(num+num);(2)
                }
                System.out.println("read thread....");
            }
            
        }
    }
    
    public static class Writethread extends Thread {
        public void run() {
             num = 2;(3)
             ready = true;(4)
             System.out.println("writeThread set over...");
        }
    }
 
    private static int num =0;
    private static boolean ready = false;
    
    public static void main(String[] args) throws InterruptedException {
 
        ReadThread rt = new ReadThread();
        rt.start();
        
        Writethread  wt = new Writethread();
        wt.start();
        
        Thread.sleep(10);
        rt.interrupt();
        System.out.println("main exit");
    }
 

      如程式碼由於(1)(2)(3)(4) 之間不存在依賴,所以寫執行緒(3)(4)可能被重排序為先執行(4)在執行(3),那麼執行(4)後,讀執行緒可能已經執行了(1)操作,並且在(3)執行前開始執行(2)操作,這時候列印結果為0而不是4.

解決:使用volatile 修飾ready可以避免重排序。