簡體   English   中英

為什么將 volatile 與同步塊一起使用?

[英]why using volatile with synchronized block?

我在 java 中看到了一些示例,它們在代碼塊上進行同步以更改某些變量,而該變量最初被聲明為 volatile。初始化那個實例......我的問題是為什么我們在同步它時聲明它是可變的,為什么我們需要同時做? 其中一個對另一個不夠用嗎??

public class SomeClass {
    volatile static Object uniqueInstance = null;

    public static Object getInstance() {
        if (uniqueInstance == null) {
            synchronized (someClass.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new SomeClass();
                }
            }
        }
        return uniqueInstance;
    }
}

提前致謝。

在這種情況下,如果第一次檢查在同步塊內,那么同步本身就足夠了(但事實並非如此,如果變量不是易失性的,一個線程可能看不到另一個線程執行的更改)。 僅使用 Volatile 是不夠的,因為您需要以原子方式執行多個操作。 但要小心! 您在這里擁有的是所謂的雙重檢查鎖定 - 一種常見的習慣用法,不幸的是它不能可靠地工作 我認為自 Java 1.6 以來這已經發生了變化,但這種代碼仍然可能存在風險。

編輯:當變量是 volatile 時,此代碼從 JDK 5(不是我之前寫的 6)開始正常工作,但在 JDK 1.4 或更早版本下將無法正常工作。

這使用了雙重檢查鎖定,請注意if(uniqueInstance == null)不在同步部分內。

如果uniqueInstance不是易失性的,則它可能會使用部分構造的對象“初始化”,其中除了在synchronized塊中執行的線程之外,它的一部分是不可見的。 在這種情況下, volatile 使其成為全有或全無的操作。

如果您沒有同步塊,則最終可能有 2 個線程同時到達這一點。

if(uniqueInstance == null) {
      uniqueInstance = new someClass(); <---- here

並且您構造了 2 個 SomeClass 對象,這違背了目的。

嚴格來說,你不需要 volatile ,方法本來可以

public static someClass getInstance() {
    synchronized(FullDictionary.class) {
         if(uniqueInstance == null) {
             uniqueInstance = new someClass();
          }
         return uniqueInstance;
    }
}

但這會導致執行 getInstance() 的每個線程的同步和序列化。

這篇文章解釋了 volatile 背后的想法。

它也在開創性工作Java Concurrency in Practice 中得到解決

主要思想是並發不僅涉及共享狀態的保護,還涉及線程之間該狀態的可見性:這就是 volatile 的用武之地。(這個更大的契約由Java 內存模型定義。)

您可以在不使用同步塊的情況下進行同步。 沒有必要在其中使用 volatile 變量...... volatile 從主內存更新一個變量..和同步更新從主內存訪問的所有共享變量..所以你可以根據你的要求使用它..

我的兩分錢在這里

首先快速解釋一下這段代碼的直覺

if(uniqueInstance == null) {
        synchronized(someClass.class) {
            if(uniqueInstance == null) {
                uniqueInstance = new someClass();
            }
        }
    }

它檢查 uniqueInstance == null 兩次的原因是為了減少調用相對較慢的 synchronized 塊的開銷。 所謂的雙重檢查鎖定。

其次,它使用synchronized的原因很容易理解,它使synchronized塊內部的兩個操作原子化。

最后, volatile 修飾符確保所有線程看到相同的副本,因此同步塊之外的第一個檢查將以與同步塊“同步”的方式看到 uniqueInstance 的值。 如果沒有 volatile 修飾符,一個線程可以為 uniqueInstance 分配一個值,但另一個線程在第一次檢查時可能看不到它。 (雖然第二次檢查會看到)

暫無
暫無

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

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