再有人問你volatile是什麼,就把這篇文章發給他

Java語言爲了解決併發編程中存在的原子性、可見性和有序性問題,提供了一系列和併發處理相關的關鍵字,好比synchronized、volatile、final、concurren包等。在前一篇文章中,咱們也介紹了synchronized的用法及原理。本文,來分析一下另一個關鍵字——volatile。算法

本文就圍繞volatile展開,主要介紹volatile的用法、volatile的原理,以及volatile是如何提供可見性和有序性保障的等。編程

volatile這個關鍵字,不只僅在Java語言中有,在不少語言中都有的,並且其用法和語義也都是不盡相同的。尤爲在C語言、C++以及Java中,都有volatile關鍵字。均可以用來聲明變量或者對象。下面簡單來介紹一下Java語言中的volatile關鍵字。緩存

volatile的用法性能優化

volatile一般被比喻成"輕量級的synchronized",也是Java併發編程中比較重要的一個關鍵字。和synchronized不一樣,volatile是一個變量修飾符,只能用來修飾變量。沒法修飾方法及代碼塊等。多線程

volatile的用法比較簡單,只須要在聲明一個可能被多線程同時訪問的變量時,使用volatile修飾就能夠了。架構

public class Singleton {併發

private volatile static Singleton singleton;分佈式

private Singleton (){}微服務

public static Singleton getSingleton() {高併發

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}

如以上代碼,是一個比較典型的使用雙重鎖校驗的形式實現單例的,其中使用volatile關鍵字修飾可能被多個線程同時訪問到的singleton。

volatile的原理

在再有人問你Java內存模型是什麼,就把這篇文章發給他中咱們曾經介紹過,爲了提升處理器的執行速度,在處理器和內存之間增長了多級緩存來提高。可是因爲引入了多級緩存,就存在緩存數據不一致問題。

可是,對於volatile變量,當對volatile變量進行寫操做的時候,JVM會向處理器發送一條lock前綴的指令,將這個緩存中的變量回寫到系統主存中。

可是就算寫回到內存,若是其餘處理器緩存的值仍是舊的,再執行計算操做就會有問題,因此在多處理器下,爲了保證各個處理器的緩存是一致的,就會實現緩存一致性協議

緩存一致性協議:每一個處理器經過嗅探在總線上傳播的數據來檢查本身緩存的值是否是過時了,當處理器發現本身緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,當處理器要對這個數據進行修改操做的時候,會強制從新從系統內存裏把數據讀處處理器緩存裏。

因此,若是一個變量被volatile所修飾的話,在每次數據變化以後,其值都會被強制刷入主存。而其餘處理器的緩存因爲遵照了緩存一致性協議,也會把這個變量的值從主存加載到本身的緩存中。這就保證了一個volatile在併發編程中,其值在多個緩存中是可見的。

volatile與可見性

可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。

咱們在再有人問你Java內存模型是什麼,就把這篇文章發給他中分析過:Java內存模型規定了全部的變量都存儲在主內存中,每條線程還有本身的工做內存,線程的工做內存中保存了該線程中是用到的變量的主內存副本拷貝,線程對變量的全部操做都必須在工做內存中進行,而不能直接讀寫主內存。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量的傳遞均須要本身的工做內存和主存之間進行數據同步進行。因此,就可能出現線程1改了某個變量的值,可是線程2不可見的狀況。

前面的關於volatile的原理中介紹過了,Java中的volatile關鍵字提供了一個功能,那就是被其修飾的變量在被修改後能夠當即同步到主內存,被其修飾的變量在每次是用以前都從主內存刷新。所以,可使用volatile來保證多線程操做時變量的可見性。

volatile與有序性

有序性即程序執行的順序按照代碼的前後順序執行。

咱們在再有人問你Java內存模型是什麼,就把這篇文章發給他中分析過:除了引入了時間片之外,因爲處理器優化和指令重排等,CPU還可能對輸入代碼進行亂序執行,好比load->add->save 有可能被優化成load->save->add 。這就是可能存在有序性問題。

而volatile除了能夠保證數據的可見性以外,還有一個強大的功能,那就是他能夠禁止指令重排優化等。

普通的變量僅僅會保證在該方法的執行過程當中所依賴的賦值結果的地方都能得到正確的結果,而不能保證變量的賦值操做的順序與程序代碼中的執行順序一致。

volatile能夠禁止指令重排,這就保證了代碼的程序會嚴格按照代碼的前後順序執行。這就保證了有序性。被volatile修飾的變量的操做,會嚴格按照代碼順序執行,load->add->save 的執行順序就是:load、add、save。

volatile與原子性

原子性是指一個操做是不可中斷的,要所有執行完成,要不就都不執行。

咱們在Java的併發編程中的多線程問題究竟是怎麼回事兒中分析過:線程是CPU調度的基本單位。CPU有時間片的概念,會根據不一樣的調度算法進行線程調度。當一個線程得到時間片以後開始執行,在時間片耗盡以後,就會失去CPU使用權。因此在多線程場景下,因爲時間片在線程間輪換,就會發生原子性問題。

在上一篇文章中,咱們介紹synchronized的時候,提到過,爲了保證原子性,須要經過字節碼指令monitorenter和monitorexit,可是volatile和這兩個指令之間是沒有任何關係的。

因此,volatile是不能保證原子性的。

在如下兩個場景中可使用volatile來代替synchronized:

一、運算結果並不依賴變量的當前值,或者可以確保只有單一的線程會修改變量的值。

二、變量不須要與其餘狀態變量共同參與不變約束。

除以上場景外,都須要使用其餘方式來保證原子性,如synchronized或者concurrent包。

咱們來看一下volatile和原子性的例子:

public class Test {

public volatile int i = 0;

public void increase() {

i++;

}

public static void main(String[] args) {

final Test test = new Test();

for(int i=0;i<10;i++){

new Thread(){

public void run() {

for(int j=0;j<1000;j++)

test.increase();

};

}.start();

}

while(Thread.activeCount()>1) //保證前面的線程都執行完

Thread.yield();

System.out.println(test.i);

}

}

以上代碼比較簡單,就是建立10個線程,而後分別執行1000次i++操做。正常狀況下,程序的輸出結果應該是10000,可是,屢次執行的結果都小於10000。這其實就是volatile沒法知足原子性的緣由。

爲何會出現這種狀況呢,那就是由於雖然volatile能夠保證i在多個線程之間的可見性。可是沒法保證i++的原子性。

i++操做,一共有三個步驟:load i ,add i ,save i。在多線程場景中,若是這三個步驟沒法按照順序執行的話,那麼就會出現問題。

如上圖,兩個線程同時執行i++操做,若是容許指令重排,咱們指望的結果是3,可是實際執行結果多是2,甚至多是1。

總結與思考

咱們介紹過了volatile關鍵字和synchronized關鍵字。如今咱們知道,synchronized能夠保證原子性、有序性和可見性。而volatile卻只能保證有序性和可見性。在這裏順便給你們推薦一個架構交流羣:617434785,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源。相信對於已經工做和遇到技術瓶頸的碼友,在這個羣裏會有你須要的內容。

那麼,咱們再來看一下雙重校驗鎖實現的單例,已經使用了synchronized,爲何還須要volatile?

public class Singleton {

private volatile static Singleton singleton;

private Singleton (){}

public static Singleton getSingleton() {

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}

相關文章
相關標籤/搜索