簡體   English   中英

同步塊中易失性數組寫入的必要性

[英]Necessity of volatile array write while in synchronized block

有關JMM的問題以及有關在同步塊中寫入但是讀取未同步的易失性字段的語義。

在下面代碼的初始版本中,我沒有同步訪問,因為它不需要早期的需求(並且濫用自我賦值this.cache = this.cache確保了volatile寫入語義)。 某些要求已更改,需要同步以確保不發送重復更新。 我的問題是同步塊是否排除了要求自動分配易失性字段?

  // Cache of byte[] data by row and column.
  private volatile byte[][][] cache;

  public byte[] getData(int row, int col)
  {
    return cache[row][col];
  }

  public void updateData(int row, int col, byte[] data)
  {
    synchronized(cache)
    {
      if (!Arrays.equals(data,cache[row][col]))
      {
        cache[row][col] = data;

        // Volatile write.
        // The below line is intentional to ensure a volatile write is
        // made to the array, since access via getData is unsynchronized.
        this.cache = this.cache;

        // Notification code removed
        // (mentioning it since it is the reason for synchronizing).
      }
    }
  }

如果沒有同步,我認為自我分配易失性寫入在技術上是必要的(盡管IDE將其標記為無效)。 使用synchronized塊,我認為它仍然是必要的(因為讀取是不同步的),但我只想確認,因為如果它實際上不需要它在代碼中看起來很荒謬。 我不確定在同步塊結束和易失性讀取之間是否有任何保證我不知道。

是的,根據Java Memory Model,你仍然需要volatile寫。 解鎖cache到后續易失性cache讀取沒有同步順序: unlock - > volatileRead不保證可見性。 你需要解鎖 - > lockvolatileWrite - > volatileRead

但是,真正的JVM具有更強大的內存保證。 通常unlockvolatileWrite具有相同的記憶效應(即使它們在不同的變量上); lockvolatileRead相同。

所以我們在這里陷入兩難境地。 典型的建議是你應該嚴格遵守規范。 除非你對此事有廣泛的了解。 例如,JDK代碼可能會使用一些理論上不正確的技巧; 但代碼針對特定的JVM,作者是專家。

無論如何,額外的易失寫入的相對開銷似乎並不大。

您的代碼是正確和有效的; 但它不在典型模式之內; 我會調整它有點像:

  private final    byte[][][] cacheF = new ...;  // dimensions fixed?
  private volatile byte[][][] cacheV = cacheF;

  public byte[] getData(int row, int col)
  {
    return cacheV[row][col];
  }

  public void updateData(int row, int col, byte[] data)
  {
    synchronized(cacheF)
    {
      if (!Arrays.equals(data,cacheF[row][col]))
      {
        cacheF[row][col] = data;

        cacheV = cacheF; 
      }
    }
  }

自我賦值確保另一個線程將讀取已設置的數組引用,而不是另一個數組引用。 但是你可能有一個線程修改數組而另一個線程讀取它。

應該同步對數組的讀取和寫入。 此外,我不會盲目地存儲和返回緩存中的數組。 數組是一個可變的,非線程安全的數據結構,任何線程都可能通過改變數組來破壞緩存。 您應該考慮創建防御性副本,和/或返回不可修改的列表而不是字節數組。

對易失性數組的索引寫入實際上沒有記憶效應。 也就是說,如果您已經實例化了數組,那么將字段聲明為volatile將不會為您提供在分配給數組中的元素時所尋找的內存語義。

換一種說法

private volatile byte[][]cache = ...;
cache[row][col] = data;

具有與內存相同的內存語義

private final byte[][]cache = ...;
cache[row][col] = data;

因此,您必須同步對陣列的所有讀取和寫入。 當我說'相同的內存語義'時,我的意思是不能保證線程會讀取cache[row][col]最新值cache[row][col]

暫無
暫無

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

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