AtomicInteger,一個提供原子操做的Integer的類。在Java語言中,++i和i++操做並非線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則經過一種線程安全的加減操做接口。 java
要使用多處理器系統的功能,一般須要使用多線程構造應用程序。可是正如任何編寫併發應用程序的人能夠告訴你的那樣,要得到好的硬件利用率,只是簡單地在多個線程中分割工做是不夠的,還必須確保線程確實大部分時間都在工做,而不是在等待更多的工做,或等待鎖定共享數據結構。而synchronized來控制併發就須要去等待這個鎖資源,這步是很是消耗資源的,處理的吞吐量也就下去了。而java的concurrent 併發包提供的AtomicInteger就提供CAS原理避免了鎖等待,具體的實現是經過UnSafe類來直接操做底層的硬件資源。
算法
cpu 硬件同步原語(compare and swap)
支持併發的第一個處理器提供原子的測試並設置操做,一般在單位上運行這項操做。如今的處理器(包括 Intel 和 Sparc 處理器)使用的最通用的方法是實現名爲 比較並轉換或 CAS 的原語。(在 Intel 處理器中,比較並交換經過指令的 cmpxchg 系列實現。PowerPC 處理器有一對名爲「加載並保留」和「條件存儲」的指令,它們實現相同的目地;MIPS 與 PowerPC 處理器類似,除了第一個指令稱爲「加載連接」。)
CAS 操做包含三個操做數 —— 內存位置(V)、預期原值(A)和新值(B)。若是內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值。不然,處理器不作任何操做。不管哪一種狀況,它都會在 CAS 指令以前返回該位置的值。(在 CAS 的一些特殊狀況下將僅返回 CAS 是否成功,而不提取當前值。)CAS 有效地說明了「我認爲位置 V 應該包含值 A;若是包含該值,則將 B 放到這個位置;不然,不要更改該位置,只告訴我這個位置如今的值便可。」
一般將 CAS 用於同步的方式是從地址 V 讀取值 A,執行多步計算來得到新值 B,而後使用 CAS 將 V 的值從 A 改成 B。若是 V 處的值還沒有同時更改,則 CAS 操做成功。
相似於 CAS 的指令容許算法執行讀-修改-寫操做,而無需懼怕其餘線程同時修改變量,由於若是其餘線程修改變量,那麼 CAS 會檢測它(並失敗),算法能夠對該操做從新計算。CAS 操做的行爲(而不是性能特徵),可是 CAS 的價值是它能夠在硬件中實現,而且是極輕量級的(在大多數處理器中)。 安全
普通的類: 數據結構
public class SynchronizedCounter { private int value; public synchronized int getValue(){ return value; } public synchronized int getNextValue(){ return value++; } public synchronized int getPreviousValue(){ return value--; } }增長synchronized關鍵字的類:
public class SynchronizedCounter { private int value; public synchronized int getValue(){ return value; } public synchronized int getNextValue(){ return value++; } public synchronized int getPreviousValue(){ return value--; } }使用原子變量的類:
import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private final AtomicInteger value = new AtomicInteger(0); public int getValue(){ return value.get(); } public int getNextValue(){ return value.incrementAndGet(); } public int getPreviousValue(){ return value.decrementAndGet(); } }測試類:
public class Test { static SynchronizedCounter counteSynchronizedCounter = new SynchronizedCounter(); static AtomicCounter atomicCounter = new AtomicCounter(); public static long startTime = 0; public static long endTime = 0; public static void main(String[] args) { startTime = System.currentTimeMillis(); for (int i = 0; i < 1 ; i++) { Thread t = new Thread(runnable); t.start(); } } static Runnable runnable = new Runnable() { @Override public void run() { for(int i=0;i<100000000;i++){ counteSynchronizedCounter.getNextValue(); } endTime = System.currentTimeMillis(); System.out.println(endTime-startTime); } }; //開三個線程用時56936,開一個線程4368 // static Runnable runnable = new Runnable() { // @Override // public void run() { // for(int i=0;i<100000000;i++){ // atomicCounter.getNextValue(); // } // endTime = System.currentTimeMillis(); // System.out.println(endTime-startTime); // } // }; //開三個線程用時13323,一個線程用時2288 }上面的結果說明了原子變量比synchronized關鍵字的運行效率高多了。
這裏解釋一下, 「相似於 CAS 的指令容許算法執行讀-修改-寫操做,而無需懼怕其餘線程同時修改變量,由於若是其餘線程修改變量,那麼 CAS 會檢測它(並失敗),算法能夠對該操做從新計算。」 多線程
CAS 操做包含三個操做數 —— 內存位置(V)、預期原值(A)和新值(B)。若是內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值。不然,處理器不作任何操做。而後對該操做從新計算,這裏解釋一下AtomicInteger是怎麼從新計算的? 併發
AtomicInteger中的源碼: ide
這裏使用了一個無限循環,只要沒有正確返回數據就一直循環。
性能
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }