1. 程式人生 > 其它 >【Java學習筆記(九十一)】之 volatile關鍵字

【Java學習筆記(九十一)】之 volatile關鍵字

技術標籤:Java學習筆記# 多執行緒多執行緒java併發程式設計

本文章由公號【開發小鴿】釋出!歡迎關注!!!


老規矩–妹妹鎮樓:

一. Volatile關鍵字

(一) 定義

Java允許執行緒訪問共享變數,為了確保共享變數能被準確和一致地更新,執行緒應該確保通過排它鎖單獨獲得這個變數。即只要變數被volatile修飾了,則Java可以確保所有執行緒看到這個變數的值都是一致的,某個執行緒對該變數進行更新時,則其他執行緒也能看到最新的,這就是記憶體可見性。

(二) volatile實現記憶體可見性的過程

執行緒寫volatile變數的過程:

  1. 改變執行緒工作記憶體中該變數副本;

  2. 將改變後的副本從工作記憶體重新整理到主記憶體;

執行緒讀volatile變數的過程:

  1. 從主記憶體中讀取volatile變數的最新值到執行緒的工作記憶體;

  2. 從工作記憶體讀取volatile變數的副本;

(三) volatile實現記憶體可見性原理

寫操作時,通過在寫操作指令後新增一條store屏障指令,讓工作記憶體的副本重新整理到主記憶體;

讀操作時,通過在讀操作前新增load屏障指令,及時讀取到主記憶體的值;

記憶體屏障是一種CPU指令,用於控制指定條件下的重排序和記憶體可見性問題,Java編譯器也會根據記憶體屏障的規則禁止重排序。

(四) volatile不具備原子性

volatile能夠實現可見性,有序性,但是沒有實現原子性,可以通過以下的方案解決:

1. 使用synchronized修飾修改共享變數的程式碼段,使該段程式碼具有原子性

public synchronized void addCount(){
	cout++;
}

2. 使用ReentrantLock(可重入鎖),手動加鎖與解鎖,效能比synchronized更好

private Lock lock = new ReentrantLock();
public void addCount(){
	lock.lock();
	count++;
	lock.unlock
(); }

3. 使用AtomicInteger(原子操作),該型別使用CAS操作,代替原有的Integer類,能夠實現原子性,且底層沒有使用鎖,效能最高,推薦使用。當我們操作的型別有執行緒安全問題時,可以使用這種原子型別,以Atomic開頭的基本資料型別。

public static AtomicInteger count = new AtomicInteger(0);
public void addCount(){
	count.incrementAndGet();
}

(五) volatile的使用場景

首先得是一個多執行緒的環境,其次多執行緒中不同執行緒對於該變數有讀寫操作,這樣記憶體一致性才有發揮的空間,最後是能夠使用其他的方法來解決volatile的原子性問題。總的來說是該變數要獨立於其他的變數和該變數以前的值。


(六) 與synchronized的比較

synchronized是一個重量級的鎖,volatile是輕量級的,成本更低,因為volatile不會引起執行緒上下文的切換和排程。且synchronized底層實現中使用作業系統的互斥鎖實現的,volatile的底層實現中沒有使用鎖,因此效率更高。

synchronized能夠實現原子性,記憶體可見性,有序性;volatile只能實現有序性,記憶體可見性。