volatile簡介

volatile簡介

java語言提供了一種稍弱的內存同步機制,即volatile變量。用來確保將變量的更新操做通知到其它線程,保證了新值能當即同步到主內存,以及每次使用前當即從內存刷新。當變量聲明爲volatile類型後,編譯器與運行時都會注意到這個變量是共享的。java

volatile變量對全部線程可見,對volatile變量全部的寫操做都能當即反應到其它的線程中,可是volatile並不能保證操做的原子性,所以volatile變量的運算並非線程安全的。編程

併發編程中的三個概念

在併發編程中,咱們一般會遇到如下三個問題:原子問題,可見性問題,有序性問題。緩存

原子性:

即一個操做或多個操做要麼所有執行而且執行過程當中不能被任何操做打斷,要麼都不執行。只有簡單的讀取、賦值(並且必須是將數字賦值給某個變量,變量 之間的賦值不是原子操做)纔是原子操做。java內存模型只保證了基本讀取和賦值是原子操做,若是要實現更大範圍操做的原子性,能夠經過 synchronized和lock來實現。因爲synchronized和lock能保證任一時刻只有一個線程執行該程序塊,那麼天然就不存在原子性問 題了,從而保證了原子性。安全

可見性:

可見性是指多個線程訪問同一個變量時,一個線程修改了這個變量的值,其它線程可以當即看到修改的值。對於可見性,java提供了volatile關 鍵字來保證可見性。當一個共享變量被volatile修飾的時候,它會保證修改的值當即被更新到內存,當有其它線程須要讀取時,它會去內存中去讀新值。而 普通共享變量不能保證可見性。由於普通共享變量被修改後,何時被寫入內存是不肯定的。當其餘線程去讀取新值時,此時內存中可能仍是本來的值,所以沒法 保證可見性。多線程

有序性:

即程序執行的順序按照代碼的前後順序執行。在java內存模型中,容許編譯器和處理器對執行進行重排,可是重排過程不會影響單線程程序的執行,卻會 影響到多線程併發執行的正確性。在java中,能夠經過volatile關鍵字來保證必定的有序性。另外能夠經過synchronized和lock來保 證有序性。很顯然,synchronized和lock保證了每一個時刻只有一個線程執行同步代碼,至關因而讓線程執行同步代碼,天然保證了有序性。併發

指令重排:

通常來講,處理器爲了提升程序運行效率,可能會對輸入代碼進行優化,它不保證程序中各個語句的執行順序同代碼中的順序一致,可是它會保證程序最終執 行結果和代碼順序執行的結果是一致的。若是兩個語句之間沒有數據依賴,那麼可能會被重排。指令重排不會影響單個線程的執行,可是會影響到線程併發執行的正 確性。性能

在併發編程中,若是想要一個程序正確的執行,必須保證原子性、可見性以及有序性,只要有一個沒有被保證,就有可能會致使程序運行不正確。優化

volatile關鍵字的兩層含義

一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾以後,那麼就具有了兩層含義:線程

1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這個新值對其它線程來講是當即可見的。內存

2)禁止指令重排。

volatile關鍵字可以保證可見性,可是不能保證原子性。

volatile能禁止指令重排,因此volatile能在必定程度上保證有序性。

volatile關鍵字機制指令重排有兩層意思:

1.當程序執行到volatile變量的讀操做或者寫操做時,在其前面的操做的更改確定所有已經執行結束,而且結果已經對後面的修改可見,其後的操做必定尚未進行。

2.在進行指令優化時,不會對volatile變量進行重排。

從volatile關鍵字生成的彙編代碼發現,加入volatile關鍵字時,會多出一個lock前綴指令。

lock前綴指令實際上至關於一個內存屏障。內存屏障會提供三個功能:

1)它確保指令重排時不會把其後面的指令排到內存屏障以前的位置,也不會把前面的指令排到內存屏障以後,即在執行到內存屏障這句指令時,在它前面的操做已經所有完成。

2)它會強制對緩存的修改操做當即寫入內存。

3)若是是寫操做,它會致使其餘cpu中對應的緩存行無效。

使用volatile關鍵字的場景

synchronized關鍵字是防止多個線程同時執行一段代碼,那麼就會影響到程序的執行效率,而volatile關鍵字在某些狀況下性能要優於 synchronized,可是要注意volatile關鍵字沒法保證操做的原子性。一般來講,使用volatile必須具有如下兩個條件。

1)對變量的寫操做不依賴於當前值。

2)該變量沒有包含在具備其它變量的不變式中。

意思就是保證操做是原子操做,才能保證使用volatile關鍵字的程序在併發時可以正確執行。

相關文章
相關標籤/搜索