Java高階語法---Volatile

背景:據說Volatile Java高階語法亦是挺進BAT的必經之路。緩存

 

Volatile安全

volatile同步機制又涉及Java內存模型中的可見性、原子性和有序性,惡補基礎一波。ide

可見性:性能

可見性簡單的說是線程之間的可見性,一個線程修改的狀態對另外一個線程是可見對,也就是一個線程的修改結果另外一個線程能夠立刻看到;但一般,咱們沒法確保執行讀操做的線程可以時時的看到其餘線程寫入的值,So爲了確保多個線程之間對內存寫入操做可見性,必須使用同步機制;如用volatile修飾的變量就具備可見性,volatile修飾的變量不容許線程內部緩存和重排序,而是直接修改內存,因此對其餘線程來講是可見的;但volatile只能保證被修飾的內容具備可見性,並不具有原子性,如volatile int vipNumber = 100,以後有一個vipNumber++ 的操做,這個變量vipNumber具備可見性,可是vipNumber++ 依然是一個非原子操做,也就是說這個操做一樣存在線程安全問題。優化

原子性:spa

原子具備不可分割的特性,如int age = 22,這個操做是不可分割的,那麼稱其爲原子操做,具備原子性;再好比age++,這個操做實際是age = age + 1,其是可分割的,So它不是一個原子操做;而非原子操做都會存在線程安全問題,須要咱們使用同步技術(synchronized)來讓它變成一個原子操做;Java的concurrent包下提供了一些原子類,如:AtomicLongMap、AtomicDouble、AtomicReference 等;在Java中用synchronized、lock和unlock來保證原子性。線程

有序性:code

Java語言提供了volatile和synchronized兩個關鍵字來保證線程之間操做的有序性,volatile是由於自己包含「禁止指令重排序」的語義,synchronized是由「一個變量在同一時刻只容許一條線程對其進行lock操做」這條規則得到有序性的,此規則決定了持有同一個對象鎖的兩個同步塊只能串行執行。對象

Volatile原理:blog

volatile是一種稍弱的同步機制,其用來確保將變量的更新操做通知到其餘線程;當變量聲明爲volatile類型後,編譯器與JVM運行時都會注意到這個變量時共享的,所以不會將此變量上的操做與其餘操做一塊兒重排序;volatile修飾後變量不會緩存在寄存器或者對其餘處理器不可見的地方,所以在單曲volatile類型的變量時總會返回最新寫入的值;除此以外,在訪問volatile變量時不會執行加鎖操做,也就不會執行線程阻塞,所以volatile變量是一種比synchronized關鍵字更輕量級的同步機制;當對非volatile變量進行讀寫時,每一個線程從內存拷貝變量到CPU緩存中,若是計算機有多個CPU則每一個線程可能在不一樣的CPU上被處理,這就意味着每一個線程均可以拷貝到不一樣的CPU緩存cache中,而不是像volatile變量那樣直接讀內存,JVM保證其每次讀變量都從內存中讀,跳過了CPU cache這一步驟。

當一個變量定義爲volatile以後,其具有的兩種特徵:

一、保證此變量對全部的線程的可見性;當一個線程修改了此變量的值,volatle保證新值可以當即同步到主內存,以及每次使用前當即從主每次刷新;

二、禁止指令重排序優化;被volatile修飾的變量賦值後多執行了一個「load」操做,此操做至關於一個內存屏障(指令重排序時不能把後面的指令重排序到內存屏障以前到位置),只有一個CPU訪問內存時,不須要內存屏障;(指令重排序:指CPU採用了容許將多條指令不按程序規定的順序分開發送給各相應電路單元處理)

另外:在性能方面,volatile的讀操做性能消耗與普通變量基本無異,可是寫操做稍慢,由於它須要在本地代碼中插入許多內存屏障來保證處理器不發生亂序執行。

這裏擼了一個例子用volatile保證線程間的同步,若是變量author不經volatile修飾,線程2中對author的值作了修改並未同步到線程1中,其一直存在緩存中。

FYI:

 1 import lombok.extern.slf4j.Slf4j;  2 
 3 @Slf4j  4 public class TestVolatile {  5     //private volatile String author = "tjt"; // volatile修飾author保證兩個線程到可見性,即不存在緩存cache中
 6     private String author = "tjt";  // 不用volatile修飾變量author則author修改值後在線程之間並不可見
 7     private boolean enable = false;  8     public static void main(String[] args) throws Exception {  9         TestVolatile testVolatile = new TestVolatile(); 10         log.info("原始定義的author: "+testVolatile.author); 11         Thread thread = new Thread( new Runnable() { 12  @Override 13             public void run() { 14  testVolatile.testMethodOne(); 15  } 16  }); 17  thread.start(); 18         thread.sleep(2000l); 19  testVolatile.testMethodTwo(); 20  } 21     public void testMethodOne() { 22         while(true) { 23             if ("detect_tjt".equals(author) && enable == false) { 24                 log.info("線程testMethodOne中檢測到來author修改成: "+author); 25                 enable = true; 26                 System.exit(0); 27  } 28  } } 29     public void testMethodTwo() { 30         author = "detect_tjt"; 31         log.info("線程testMethodTwo中把author修改成: "+author); 32  } 33 }

用volatile修飾author執行結果:
- 原始定義的author: tjt
- 線程testMethodTwo中把author修改成: detect_tjt
- 線程testMethodOne中檢測到來author修改成: detect_tjt
無volatile修飾author執行結果:

- 原始定義的author: tjt
- 線程testMethodTwo中把author修改成: detect_tjt

相關文章
相關標籤/搜索