學習java併發編程實戰的一些心得體會(一)

內存可見性

當讀操作和寫操作在不同的線程中執行時,我們無法確保執行讀操作的線程能適時地看到其他線程寫入的值,有時甚至是根本不可能的事情,爲了確保多個線程之間對內存寫入操作的可見性,必須使用同步機制。

在程序中,NoVisibility說明了當多個線程在沒有同步的情況下共享數據時出現的錯誤。在代碼中,主線程和讀線程都將訪問共享變量ready和number。主線程啓動讀線程,然後將number設爲42,並將ready設爲true,讀線程一直循環直到發現ready的值變爲true,然後輸出number的值,雖然NoVisibility看起來會輸出42,但事實上很可能輸出0,或者根本無法終止。只是因爲在代碼中沒有使用足夠的同步機制,因此無法保證主線程寫入的ready值和number值對於讀線程來說是可見的。


public class NoVisibility {

    private static boolean ready;
    private static int     number;

    private static class ReaderThread extends Thread {

        public void run() {
            while (!ready) {
                Thread.yield();
            }
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}



NoVisibility 可能會持續循環下去,因爲讀線程可能永遠都看不到ready的值。一種更奇怪的現象是,NoVisibility可能會輸出0,因爲讀線程可能看到了寫入ready的值,但卻沒有看到之後寫入number的值這種現象被稱爲“重排序”。只要在某個線程中無法檢測到重排序情況,那個就無法確保線程中的操作將按照程序中指定的順序來執行。當主線程首先寫入number,然後在沒有同步的情況下寫入ready,那麼讀線程看到的順序可能與寫入的順序完全相反。


在沒有同步的情況下,編譯器、處理器、以及運行時等都可以對操作的執行順序進行一些意想不到的調整。在缺乏足夠同步的多線程程序中,要想對內存操作的執行順序進行判斷,幾乎無法得出正確的結論

非原子的64位操作

當線程在沒有同步的情況下讀取變量時,可能會得到一個失效的值,單至少這個值是由之前某個線程設置的值,而不是一個隨機值。這種安全性保證也被稱爲最低安全性。
 
 
最低安全性適用於絕大多數變量,但是存在一個例外:非volatile 類型的64位數值變量(double和long)。Java內存模型要求,變量的讀取操作和寫入操作都必須是原子操作,但對於非volatile類型的long和double變量,JVM允許將64位的讀操作或寫操作分解爲兩個32位操作。當讀取一個非volatile類型的long變量時,如果對該變量的讀操作和寫操作在不同的線程中執行,那麼和可能會讀到某個值的高32位和另一個值的低32位。因此,即使不考慮失效數據問題,在多線程程序中使用共享且可變的long和double等類型的變量也是不安全的除非用關鍵字volatile來聲明它們,或者用鎖保護起來



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章