簡體   English   中英

同步中斷,無可變變量

[英]Broken synchronization without volatile variable

如果程序員忘記將volatile關鍵字添加到用於同步的變量中,我想從高速緩存的觀點(MESI協議)中了解會發生什么。

下面的代碼片段在一些迭代之后就停止了。 我知道這是因為2個線程之一(假設THREAD 0)看不到getNext()進行的THREAD 1對current變量的更新,因此它永遠循環。

但是,我不明白為什么會這樣。 THREAD 0在current循環,並且應該在某個時刻看到高速緩存行已由THREAD 1更新(高速緩存行已切換為“修改”狀態),並在內存總線上發出“讀取”消息以在其本地高速緩存中獲取它。 current變量中添加volatile修飾符會使一切正常。

發生什么事情阻止THREAD 0繼續執行?

參考: 內存屏障:軟件黑客的硬件視圖

public class Volatile {
    public static final int THREAD_0 = 0;
    public static final int THREAD_1 = 1;
    public static int current;
    public static int counter = 0;

    public static void main(String[] args) {
        current = 0;

        /** Thread 0 **/
        Thread thread0 = new Thread(() -> {
            while(true) { /** THREAD_0 */
                while (current != THREAD_0);

                counter++;
                System.out.println("Thread0:" + counter);

                current = getNext(THREAD_0);
            }
        });

        /** Thread 1 **/
        Thread thread1 = new Thread(() -> {
            while(true) { /** THREAD_1 */
                while (current != THREAD_1);

                counter++;
                System.out.println("Thread1:" + counter);

                current = getNext(THREAD_1);
            }
        });

        thread0.start();
        thread1.start();
    }

    public static int getNext(int threadId) {
        return threadId == THREAD_0 ? THREAD_1 : THREAD_0;
    }
}

不同的處理器體系結構可以具有不同程度的緩存一致性。 有些可能非常少,因為目標是最大化吞吐量,並不必要地與內存同步緩存將其拖累。 Java在檢測到需要協調時會施加自己的內存障礙,但這取決於程序員是否遵循規則。

如果程序員不添加指示(例如使用synced或volatile關鍵字)可以在線程之間共享變量,則JIT可以假定沒有其他線程在修改該變量,並且可以相應地進行優化。 測試該變量的字節碼可以重新排序。 如果JIT對代碼進行了足夠的重新排序,則當硬件檢測到修改並獲取新值時,這可能無關緊要。

引用實踐3.1中的Java並發:

在沒有同步的情況下,編譯器,處理器和運行時可能會對操作似乎執行的順序產生一些完全奇怪的事情。 試圖推斷在同步不充分的多線程程序中“必須”執行內存操作的順序幾乎肯定是錯誤的。

(在JCIP書籍中有很多地方都提出了關於不充分同步代碼的推理是徒勞的。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM