1. 程式人生 > 實用技巧 >Java中的記憶體屏障是什麼

Java中的記憶體屏障是什麼

目錄

什麼是記憶體屏障

在轉載的大佬的文章既生synchronized,何生volatile中,提到了synchronized與volatile的底層實現原理的不同,synchronized本質上是一種阻塞鎖,而volatile則是使用了記憶體屏障來實現。所以在這裡對記憶體屏障進行一個簡單的介紹

為了禁止編譯器和CPU對程式碼進行重排序,在編譯器和CPU層面上都有對應的指令,這個就是記憶體屏障
編譯器的記憶體屏障只是為了告訴編譯器不要對指令進行重排序。當編譯完了以後,這種記憶體屏障就消失了,CPU並不會感知到編譯器層面的記憶體屏障。
而CPU的記憶體屏障則是CPU提供的指令,可供開發者呼叫。

Linux中的記憶體屏障

在Linux核心kfifo.c的原始碼的一個RingBuffer中,允許一個執行緒寫,一個執行緒讀(只能一寫一讀),整個程式碼沒有加任何的鎖,也沒有CAS,但是執行緒是安全的,這是如何做到的呢?

kififo在修改資料和更新指標之間,通過函式smp_wmb()插入了一個Store Barrier,從而確保了:

  • 更新指標的操作,不會被重排序到修改資料之前
  • 更新指標的時候,Store Cache被重新整理,其他CPU可以看見

JDK中的記憶體屏障

記憶體屏障是很底層的概念,對於Java開發者來說,一般用Volatile關鍵字就足夠了。但是從JDK8開始,Java在Unsafe類中提供了三個記憶體屏障函式。

public final class Unsafe{
	***

	public native void loadFence();
	public native void storeFence();
	public native void fullFence();

	***
}

這三個屏障並不是最基本的記憶體屏障。在理論層面,可以把基本的CPU記憶體屏障分為四種:

  1. LoadLoad:禁止讀和讀的重排序
  2. StoreStore:禁止寫與寫的重排序
  3. LoadStore:禁止讀和寫的重排序
  4. StoreLoad:禁止寫和讀的重排序

在JDK9中對JDK定義的三種記憶體屏障與理論層面劃分的四類記憶體屏障之間的對應進行了說明:

  • loadFence = LoadLoad+LoadStore
  • storeFence = StoreStore+LoadStore
  • fullFence = StoreStore+LoadStore+StoreLoad

由於不同的CPU架構不同,重排序的策略不同,所提供的記憶體屏障也有差異。
這裡舉一種實現Volatile語義的一種參考做法:

  1. 在volatile寫操作的前面插入一個StoreStore屏障。保證了volatile寫操作不會和之前的寫操作從排序
  2. 在volatile寫操作後面插入一個StoreLoad屏障。保證了volatile的寫操作不會和之後的讀操作重排序
  3. 在volatile讀操作後面插入一個LoadLoad屏障+LoadStore屏障。保證volatile的讀操作不會和後面的讀操作和寫操作重排序