1. 程式人生 > 其它 >Java偽共享詳解

Java偽共享詳解

技術標籤:javajava併發程式設計之美java多執行緒快取jvm

1、什麼是偽共享

在Cache內部是按行儲存的,其中每一行稱為一個Cache行。Cache行是Cache與主記憶體進行資料交換的單位,Cache行的大小一般為2的冪次數字節。

當CPU訪問某個變數時,首先會去看CPU Cache內是否有該變數,如果有則直接從中獲取,否則就直接去主記憶體裡面獲取該變數,然後把該變數所在 記憶體區域的一個Cache行大小的記憶體複製到Cache中。由於存放到Cache行的是記憶體塊而不是單個變數,所以可能會把多個變數存放到一個Cache行中。當多個執行緒同時修改一個快取行裡面的多個變數時,由於同時只能有一個執行緒操作快取行,所以相比將每個變數放到一個快取行,效能會有所下降,這就是偽共享。

2、為何會出現偽共享

偽共享的產生時因為多個變數被放入了一個快取行中,並且多個執行緒同時去寫入快取行中不同的變數。之所以多個變數被放入一個快取行,就是因為快取與記憶體交換資料的單位就是快取行,當CPU要訪問的變數沒有在快取中找到時,根據程式執行的區域性性原理,會把該變數所在記憶體中大小為快取行的記憶體放入快取行。

在單個執行緒下順序修改一個快取行中的多個變數,會充分利用程式執行的區域性性原則,從而加速了程式的執行。而在多執行緒下併發修改一個快取行中的多個變數時就會競爭快取行,從而降低程式執行效能。

3、如何避免偽共享

在JDK8之前一般都是通過位元組填充的方式來避免該問題,也就是建立一個變數時使用填充欄位填充該變數所在的快取行,這樣就避免了將多個變數存放在同一個快取行中

public final static class FilledLong {
    public volatile long value = OL;
    public long p1, p2, p3, p4, p5, p6;
}
  • 假如快取行為64位元組,那麼我們在FilledLong類裡面填充了6個long型別的變數,每個long型別的變數佔用8位元組,加上value變數的8位元組總共576位元組。另外,這裡FilledLong是一個類物件,而類物件的位元組碼的物件頭佔用8位元組,所以一個FilledLong物件實際會佔用64位元組的記憶體,這正好可以放入一個快取行。

JDK8提供了一個sun.misc.Contended註解,用來解決偽共享問題:

@sun.misc.Contended
public final static class FilledLong {
    public volatile long value = OL;
}
  • 需要注意的是,在預設情況下,@Contended註解只用於java核心類,比如rt包下的類。