Java volatile的幾種使用場景分析
回答
volatile
是一種輕量級的同步機制,它能保證共享變量的可見性,同時禁止重排序保證了操作的有序性,但是它無法保證原子性。所以使用 volatile
必須要滿足這兩個條件:
- 寫入變量不依賴當前值。
- 變量不參與與其他變量的不變性條件。
volatile
比較適合多個線程讀,一個線程寫的場合,典型的場景有如下幾個:
- 狀態(tài)標志
- 重檢查鎖定的單例模式
- 開銷較低的“讀-寫鎖”策略
詳解
volatile 使用條件
要想正確安全地使用 volatile
,必須要具備這兩個條件:
- 寫入變量不依賴當前值:變量的新值不能依賴于之前的舊值。如果變量的當前值與新值之間存在依賴關(guān)系,那么僅使用
volatile
是不夠的,因為它不能保證一系列操作的原子性。比如 i++。 - 變量不參與與其他變量的不變性條件:如果一個變量是與其他變量共同參與不變性條件的一部分,那么簡單地聲明變量為
volatile
是不夠的。
第一個條件很好理解,第二個條件這里需要解釋下。
“變量不參與與其他變量的不變性條件”,這里的“不變性條件”指的是一個或多個變量在程序執(zhí)行過程中需要保持的條件或關(guān)系,以確保程序的正確性。假設(shè)我們有兩個變量,它們需要滿足某種關(guān)系(例如,a + b = 99
)。我們需要在多線程環(huán)境下保證這種關(guān)閉在任何時候都是成立的。如果這個時候我們只是將其中一個變量聲明為 volatile
,雖然確保了這個變量的更新對其他線程立即可見,但卻不能保證這兩個變量作為一個整體滿足特定的不變性條件。在更新這兩個變量的過程中,其他線程可能會看到這些變量處于不一致的狀態(tài)。在這種情況下我們就需要使用鎖或者其他同步機制來保證這種關(guān)系的整體一致性。
volatile 使用場景
volatile
比較適合多個線程讀,一個線程寫的場合。
狀態(tài)標志
當我們需要用一個變量來作為狀態(tài)標志,控制線程的執(zhí)行流程時,使用 volatile
可以確保當一個線程修改了這個標志時,其他線程能夠立即看到最新的值。
public class TaskRunner implements Runnable { private volatile boolean running = true; // 狀態(tài)標志,控制任務(wù)是否繼續(xù)執(zhí)行 public void run() { while (running) { // 檢查狀態(tài)標志 // 執(zhí)行任務(wù) doSomething(); } } public void stop() { running = false; // 修改狀態(tài)標志,使得線程能夠停止執(zhí)行 } private void doSomething() { // 實際任務(wù)邏輯 } }
DCL 的單例模式
在實現(xiàn)單例模式時,為了保證線程安全,通常使用雙重檢查鎖定(Double-Checked Locking)模式。在這種模式中,volatile
用于避免單例實例的初始化過程中的指令重排序,確保其他線程看到一個完全初始化的單例對象,具體來說,就是使用 volatile
防止了Java 對象在實例化過程中的指令重排,確保在對象的構(gòu)造函數(shù)執(zhí)行完畢之前,不會將 instance
的內(nèi)存分配操作指令重排到構(gòu)造函數(shù)之外。
public class Singleton { // 使用 volatile 保證實例的可見性和有序性 private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { // 第一次檢查,避免不必要的同步 synchronized (Singleton.class) { // 鎖定 if (instance == null) { // 第二次檢查,確保只創(chuàng)建一次實例 instance = new Singleton(); } } } return instance; } }
開銷較低的“讀-寫鎖”策略
這種策略一般都是允許多個線程同時讀取一個資源,但只允許一個線程寫入的同步機制。這種“讀-寫鎖”非常適合讀多寫少的場景,我們可以利用 volatile
+ 鎖的機制減少公共代碼路徑的開銷。如下:
public class VolatileTest { private volatile int value; //讀,不加鎖,提供效率 public int getValue() { return value; } //寫操作,使用鎖,保證線程安全 public synchronized int increment() { return value++; } }
在 J.U.C 中,有一個采用“讀-寫鎖”方式的類:ReentrantReadWriteLock
,它包含兩個鎖:一個是讀鎖,另一個是寫鎖。
下面是偽代碼:
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class DataStructure { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final Object data = ...; // 被保護的數(shù)據(jù) public void read() { readWriteLock.readLock().lock(); // 獲取讀鎖 try { // 執(zhí)行讀操作 // 例如,讀取data的內(nèi)容 } finally { readWriteLock.readLock().unlock(); // 釋放讀鎖 } } public void write(Object newData) { readWriteLock.writeLock().lock(); // 獲取寫鎖 try { // 執(zhí)行寫操作 // 例如,修改data的內(nèi)容 } finally { readWriteLock.writeLock().unlock(); // 釋放寫鎖 } } }
- 讀操作 :多個線程可以同時持有讀鎖,因此多個線程可以同時執(zhí)行
read()
方法。 - 寫操作: 只有一個線程可以持有寫鎖,并且在持有寫鎖時,其他線程不能讀取或?qū)懭搿?/li>
這種“讀-寫鎖”策略提高了在多線程環(huán)境下對共享資源的讀取效率,尤其是在讀操作遠遠多于寫操作的情況下。但是,它也會讓我們的程序變更更加復(fù)雜,比如潛在的讀寫鎖沖突、鎖升級(從讀鎖升級到寫鎖)等問題。因此,在實際應(yīng)用中,大明哥推薦直接使用 ReentrantReadWriteLock
即可,無需頭鐵自己造輪子。
以上就是Java volatile的幾種使用場景分析的詳細內(nèi)容,更多關(guān)于Java volatile使用場景的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java.util.Collections類—emptyList()方法的使用
這篇文章主要介紹了java.util.Collections類—emptyList()方法的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11做java這么久了居然還不知道JSON的使用(一文帶你了解)
這篇文章主要介紹了做java這么久了居然還不知道JSON的使用(一文帶你了解),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07springboot如何開啟緩存@EnableCaching(使用redis)
在Spring Boot項目中集成Redis主要包括添加依賴到pom.xml、配置application.yml中的Redis連接參數(shù)、編寫配置類、在啟動類上添加@EnableCaching注解以及測試接口的查詢和緩存驗證等步驟,首先,需要在pom.xml中添加spring-boot-starter-data-redis依賴2024-11-11java數(shù)據(jù)結(jié)構(gòu)與算法之希爾排序詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)與算法之希爾排序,結(jié)合實例形式分析了希爾排序的概念、原理、實現(xiàn)方法與相關(guān)注意事項,需要的朋友可以參考下2017-05-05IDEA中application.properties的圖標顯示不正常的問題及解決方法
這篇文章主要介紹了IDEA中application.properties的圖標顯示不正常的問題及解決方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04java.text.DecimalFormat類十進制格式化
這篇文章主要為大家詳細介紹了java.text.DecimalFormat類十進制格式化的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03