今天看了下傳說中線程安全的AtomicInteger的源碼,總共只有不到300行,其中關鍵的代碼就更少了,主要有以下幾段:安全
1.value值設爲volatile,保證其內存可見性ide
private volatile int value;
值得注意的是,volatile關鍵字只是一種輕量級的同步方法,用來保證變量的內存可見性,可是並不能代替synchronizied。簡單舉個例子:測試
static volatile int count = 0; public static void main(String[] args) { for(int i=0;i<10000;i++){ new Thread(new Runnable() { @Override public void run() { count++; } }).start(); } System.out.println("count:"+count);
最終輸出結果並不必定是指望的10000,而每每是一個比10000小的數字。緣由是可能多個線程同時從主內存中讀取到最新的conut值,而後在各自的工做內存中執行了++操做,再往主存中同步的時候,就會寫入一樣的值,至關於一次++操做失效。this
2.接着看源碼,getAndSet(int newValue)方法spa
/** * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value */ public final int getAndSet(int newValue) { for (;;) { int current = get(); if (compareAndSet(current, newValue)) return current; } }
方法中並無synchronizied或者volatile關鍵字,那到底是如何實現線程安全的呢?關鍵的方法在於compareAndSet(current,newValue),源碼以下:線程
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
調用了unsafe的compareAndSwapInt方法,是利用了大名鼎鼎的CAS來實現的原子操做,雖然CAS也用到了排他鎖,可是比起synchronized,效率要高的多。code
3.getAndIncrement()方法blog
/** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }
可見跟getAndSet方法大同小異,首先get()取出當前的value值,而後+1,緊接着仍是調用了compareAndSet方法,看next和預期值是否相同,不相同則進入下一輪循環。內存
4.incrementAndGet()方法rem
/** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
與getAndIncrement相比,只是最後一行代碼有差異,一個是返回current,一個是返回next,我的理解就是利用CAS的原子操做,實現了線程安全的 i++和 ++i 。
其餘的方法也都是相似的,到底層調用unsafe包的CAS原子操做實現,很少說了,下面是作了一個測試:
public static void main(String[] args) { final AtomicInteger integer = new AtomicInteger(0); for (int i = 0; i < 10000; i++) { new Thread(new Runnable() { @Override public void run() { integer.getAndIncrement(); } }).start(); } System.out.println(integer.get());
運行,輸出10000沒問題,再運行幾遍,9999,9998都出現了,什麼狀況?說好的線程安全呢?百思不得其解,最後求助大神,給出答案,原來循環中的線程尚未執行完,就執行了打印的語句。修改代碼以下:
public static void main(String[] args) { final AtomicInteger integer = new AtomicInteger(0); for (int i = 0; i < 10000; i++) { new Thread(new Runnable() { @Override public void run() { integer.getAndIncrement(); } }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(integer.get()); }
OK,再也沒有出現以前的狀況,可見AtomicInteger確實是能保證線程安全的,不過因爲是一個無限的for循環裏面,不斷的進行比較、更新操做,所以,能夠想象,在線程數量很是大的狀況下,運行速度會比較慢。