筆者曾供職於華爲,三星,騰訊,是一個資深碼農,歡迎你們關注我,歡迎評論轉發java
對於Java併發編程,通常來講有如下的關注點:編程
其中線程的安全性問題是首要解決的問題,線程不安全,運行出來的結果和預期不一致,那就連基本要求都沒達到了。緩存
保證線程的安全性問題,本質上就是保證線程同步,實際上就是線程之間的通訊問題。咱們知道,在操做系統中線程通訊有如下幾種方式:安全
1.信號量 2.信號 3.管道 4.共享內存 5.消息隊列 6.socket多線程
java中線程通訊主要使用共享內存的方式。共享內存的通訊方式首先要關注的就是可見性和有序性。而原子性操做通常都是必要的,因此主要關注這三個問題。併發
原子性是指在一個操做中就是cpu不能夠在中途暫停而後再調度,既不被中斷操做,要不執行完成,要不就不執行。app
JMM只是保證了基本的原子性,但相似於i++之類的操做,看似是原子操做,其實裏面涉及到:socket
獲取 i 的值。性能
自增。spa
再賦值給 i。
這三步操做,因此想要實現i++這樣的原子操做就須要用到synchronize或者是lock進行加鎖處理。
若是是基礎類的自增操做可使用AtomicInteger這樣的原子類來實現(其本質是利用了CPU級別的 的CAS指令來完成的)。
其中用的最多的方法就是:incrementAndGet()以原子的方式自增。 源碼以下:
首先是得到當前的值,而後自增 +1。接着則是最核心的compareAndSet()來進行原子更新。
其邏輯就是判斷當前的值是否被更新過,是否等於current,若是等於就說明沒有更新過而後將當前的值更新爲next,若是不等於則返回false進入循環,直到更新成功爲止。
還有其中的get()方法也很關鍵,返回的是當前的值,當前值用了volatile關鍵詞修飾,保證了內存可見性。
private volatile int value;
可見性就是指當一個線程修改了線程共享變量的值,其它線程可以當即得知這個修改。
現代計算機中,因爲CPU直接從主內存中讀取數據的效率不高,因此都會對應的CPU高速緩存,先將主內存中的數據讀取到緩存中,線程修改數據以後首先更新到緩存,以後纔會更新到主內存。若是此時尚未將數據更新到主內存其餘的線程此時來讀取就是修改以前的數據。
如上圖所示。
volatile關鍵字就是用於保證內存可見性,當線程A更新了 volatile 修飾的變量時,它會當即刷新到主線程,而且將其他緩存中該變量的值清空,致使其他線程只能去主內存讀取最新值。
使用volatile關鍵詞修飾的變量每次讀取都會獲得最新的數據,無論哪一個線程對這個變量的修改都會當即刷新到主內存。
synchronize和加鎖也能能保證可見性,實現原理就是在釋放鎖以前其他線程是訪問不到這個共享變量的。可是和volatile相比開銷較大。
Java內存模型中的程序自然有序性能夠總結爲一句話:若是在本線程內觀察,全部操做都是有序的;若是在一個線程中觀察另外一個線程,全部操做都是無序的。前半句是指「線程內表現爲串行語義」,後半句是指「指令重排序」現象和「工做內存中主內存同步延遲」現象。
正常狀況下的執行順序應該是1>>2>>3。可是有時JVM爲了提升總體的效率會進行指令重排致使執行的順序多是2>>1>>3。可是JVM也不能是什麼都進行重排,是在保證最終結果和代碼順序執行結果一致的狀況下才可能進行重排。
重排在單線程中不會出現問題,但在多線程中會出現數據不一致的問題。
Java 中可使用volatile來保證順序性, 和 lock也能夠來保證有序性,和保證原子性的方式同樣,經過同一段時間只能一個線程訪問來實現的。
除了經過volatile關鍵字顯式的保證順序以外,JVM還經過happen-before原則來隱式的保證順序性。
其中有一條就是適用於volatile關鍵字的,針對於volatile關鍵字的寫操做確定是在讀操做以前,也就是說讀取的值確定是最新的。
volatile 的應用
雙重檢查鎖的單例模式
能夠用volatile實現一個雙重檢查鎖的單例模式:
這裏的volatile關鍵字主要是爲了防止指令重排。 若是不用volatile,singleton = new Singleton();,這段代碼實際上是分爲三步:
分配內存空間。(1)
初始化對象。(2)
將singleton對象指向分配的內存地址。(3)
加上volatile是爲了讓以上的三步操做順序執行,反之有可能第二步在第三步以前被執行就有可能某個線程拿到的單例對象是尚未初始化的,以至於報錯。
控制中止線程的標記
這裏若是沒有用 volatile 來修飾 flag ,就有可能其中一個線程調用了stop()方法修改了 flag 的值並不會當即刷新到主內存中,致使這個循環並不會當即中止。
這裏主要利用的是volatile的內存可見性。
volatile關鍵字只能保證可見性,順序性,不能保證原子性。
筆者曾供職於華爲,三星,騰訊,是一個資深碼農,歡迎你們《關注》我,歡迎評論轉發