最近在看LinkedBlockingQueue看到了其中的count使用AtomicInteger修飾,以前也看過AtomicInteger的一些解釋,也是似懂非懂的,今天深刻的瞭解了其實現方式,學到了不少東西。html
要對AtomicInteger有一個深刻的認識,就必需要了解一下悲觀鎖和樂觀鎖。cpu是時分複用的,也就是把cpu的時間片,分配給不一樣的線程/進程輪流執行,時間片與時間片之間,須要進行cpu切換,也就是會發生進程的切換。切換涉及到清空寄存器,緩存數據。而後從新加載新的thread所需數據。當一個線程被掛起時,加入到阻塞隊列,在必定的時間或條件下,在經過notify(),notifyAll()喚醒回來。在某個資源不可用的時候,就將cpu讓出,把當前等待線程切換爲阻塞狀態。等到資源(好比一個共享數據)可用了,那麼就將線程喚醒,讓他進入runnable狀態等待cpu調度。這就是典型的悲觀鎖的實現。獨佔鎖是一種悲觀鎖,synchronized就是一種獨佔鎖,它假設最壞的狀況,而且只有在確保其它線程不會形成干擾的狀況下執行,會致使其它全部須要鎖的線程掛起,等待持有鎖的線程釋放鎖。算法
可是,因爲在進程掛起和恢復執行過程當中存在着很大的開銷。當一個線程正在等待鎖時,它不能作任何事,因此悲觀鎖有很大的缺點。舉個例子,若是一個線程須要某個資源,可是這個資源的佔用時間很短,當線程第一次搶佔這個資源時,可能這個資源被佔用,若是此時掛起這個線程,可能馬上就發現資源可用,而後又須要花費很長的時間從新搶佔鎖,時間代價就會很是的高。數組
因此就有了樂觀鎖的概念,他的核心思路就是,每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試,直到成功爲止。在上面的例子中,某個線程能夠不讓出cpu,而是一直while循環,若是失敗就重試,直到成功爲止。因此,當數據爭用不嚴重時,樂觀鎖效果更好。好比咱們要說的AtomicInteger底層同步CAS就是一種樂觀鎖思想的應用。緩存
CAS就是Compare and Swap的意思,比較並操做。不少的cpu直接支持CAS指令。CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次嘗試。CAS有3個操做數,內存值V,預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。安全
在CAS操做中,會出現ABA問題。就是若是V的值先由A變成B,再由B變成A,那麼仍然認爲是發生了變化,並須要從新執行算法中的步驟。有簡單的解決方案:不是更新某個引用的值,而是更新兩個值,包括一個引用和一個版本號,即便這個值由A變爲B,而後爲變爲A,版本號也是不一樣的。多線程
Atomic包下類的理解:併發
Atomic包是Java.util.concurrent下的另外一個專門爲線程安全設計的Java包,包含多個原子操做類。這個包裏面提供了一組原子變量類。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具備排他性,即當某個線程進入方法,執行其中的指令時,不會被其餘線程打斷,而別的線程就像自旋鎖同樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另外一個線程進入,這只是一種邏輯上的理解。其實是藉助硬件的相關指令來實現的,不會阻塞線程(或者說只是在硬件級別上阻塞了)。能夠對基本數據、數組中的基本數據、對類中的基本數據進行操做。原子變量類至關於一種泛化的volatile變量,可以支持原子的和有條件的讀-改-寫操做。—— 引自@chenzehe 的博客。性能
先來看一下AtomicInteger中getAndIncrement()方法的實現:this
1 public final int getAndIncrement() { 2 for (;;) { 3 int current = get(); 4 int next = current + 1; 5 if (compareAndSet(current, next)) 6 return current; 7 } 8 }
這個方法的作法爲先獲取到當前的 value 屬性值,而後將 value 加 1,賦值給一個局部的 next 變量,然而,這兩步都是非線程安全的,可是內部有一個死循環,不斷去作compareAndSet操做,直到成功爲止,也就是修改的根本在compareAndSet方法裏面,compareAndSet()方法的代碼以下:spa
1 public final boolean compareAndSet(int expect, int update) { 2 return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 3 }
compareAndSet 傳入的爲執行方法時獲取到的 value 屬性值,next 爲加 1 後的值, compareAndSet所作的爲調用 Sun 的 UnSafe 的 compareAndSwapInt 方法來完成,此方法爲 native 方法,compareAndSwapInt 基於的是CPU 的 CAS指令來實現的。因此基於 CAS 的操做可認爲是無阻塞的,一個線程的失敗或掛起不會引發其它線程也失敗或掛起。而且因爲 CAS 操做是 CPU 原語,因此性能比較好。
下面以具體的例子分析下AtomicInteger的實現:
計數器(Counter),採用Java裏比較方便的鎖機制synchronized關鍵字,初步的代碼以下:
1 public class Counter { 2 private int value; 3 4 public synchronized int getValue() { 5 return value; 6 } 7 8 public synchronized int increment() { 9 return ++value; 10 } 11 12 public synchronized int decrement() { 13 return --value; 14 } 15 }
synchronized關鍵字是基於阻塞的鎖機制,也就是說當一個線程擁有鎖的時候,訪問同一資源的其它線程須要等待,直到該線程釋放鎖,這也就咱們前面所說的悲觀鎖。這裏會有些問題:首先,若是被阻塞的線程優先級很高很重要怎麼辦?其次,若是得到鎖的線程一直不釋放鎖怎麼辦?(這種狀況是很是糟糕的)。還有一種狀況,若是有大量的線程來競爭資源,那CPU將會花費大量的時間和資源來處理這些競爭(事實上CPU的主要工做並不是這些),同時,還有可能出現一些例如死鎖之類的狀況,最後,其實鎖機制是一種比較粗糙,粒度比較大的機制,相對於像計數器這樣的需求有點兒過於笨重,所以,對於這種需求咱們期待一種更合適、更高效的線程安全機制。
下面咱們就以模擬CAS機制來實現Counter的例子:
CAS類:
1 public class SimpleCAS { 2 private volatile int value; 3 public synchronized int getValue(){ 4 return value; 5 } 6 public synchronized boolean comperaAndSwap(int expectedValue,int newValue){ 7 int oldValue = value; 8 if(oldValue == expectedValue){ 9 value = newValue; 10 return true; 11 }else{ 12 return false; 13 } 14 } 15 }
CASCounter類:
1 public class CASCounter { 2 private SimpleCAS cas; 3 public int getValue(){ 4 return cas.getValue(); 5 } 6 public int increment(){ 7 int olevalue = cas.getValue(); 8 for (; ;) { 9 if(cas.comperaAndSwap(olevalue, olevalue+1)){ 10 return cas.getValue(); 11 } 12 } 13 14 } 15 }
上面的模擬不是CSA的真正實現,其實咱們在語言層面是沒有作任何同步的操做的,你們也能夠看到源碼沒有任何鎖加在上面,可它爲何是線程安全的呢?這就是Atomic包下這些類的奧祕:語言層面不作處理,咱們將其交給硬件—CPU和內存,利用CPU的多處理能力,實現硬件層面的阻塞,再加上volatile變量的特性便可實現基於原子操做的線程安全。因此說,CAS並非無阻塞,只是阻塞並不是在語言、線程方面,而是在硬件層面,因此無疑這樣的操做會更快更高效!
總結一下,AtomicInteger基於衝突檢測的樂觀併發策略。 能夠想象,這種樂觀在線程數目很是多的狀況下,失敗的機率會指數型增長。
參考內容:http://blog.csdn.net/zhangerqing/article/details/43057799,http://www.mamicode.com/info-detail-862009.html