不少狀況下咱們只是須要簡單的,高效,線程安全的遞增遞減方法。注意,這裏有三個條件:簡單,意味着程序員儘量少的底層或者實現起來比較簡單;高效,意味着耗用資源要少,程序處理速度要快; 線程安全也很是重要,這個在多線程下能保證數據的正確性。這三個條件看起來比較簡單,可是實現起來卻難以使人滿意。html
一般狀況下,在Java裏面, ++i或者-- i不是線程安全的,這裏面有三個獨立的操做:讀取變量當前值,爲該值+1 /-1,而後寫會新的值。在沒有額外資源能夠利用的狀況下,只能使用加鎖才能保證讀-改-寫這三個操做證的‘"原子性"。java
在J.U.C(Doug Lea)爲加入jdk以前,是採用純Java實現的,因而不可避免的採用了synchronized關鍵字。程序員
public final synchronized void set(int newValue); public final synchronized int getAndSet(int newValue); public final synchronized int incrementAndGet();
同時在變量上使用了volatile(後面詳述)來保證get()的時候不用加鎖。儘管synchronized的代價仍是很高,可是在沒有JNI的手段下純Java語言仍是不能實現此操做的。安全
來看看java.util.cocurrent.atomic.AtomicInteger開始:多線程
int addAndGet(int delta); //以原子方式將給定值與當前值想家。實際上就是等於線程安全版本的 i += delta boolean compareAndSet(int expect, int update); //若是當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。若是 //更新成功就返回true, 不然就返回false, 而且不修改原值 int decrementAndGet(); //以原子方式將當前值減1,至關於線程安全版本的--i操做。 int get(); //得到當前值。 int getAndAdd(int delta); //以原子方式將給定值與當前值相加,至關於線程版本的 t = i; i += //delat; return t; int getAndDecrement(); //以原子方式將當前值減1,至關於線程安全版本的i--; int getAndIncrement(); //以原子方式將當前值加1,至關於線程安全版本的i++; int getAndSet(); //以原子方式設定給定值,並返回舊值; int incrementAndGet(); //以原子方式將當前值加1,至關於線程安全版本的++i;
看幾個實現:測試
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value;
調用sun.misc.Unsafe類實現cas操做(歷史版權緣由,Unsafe源碼看不了),獲取該對象value成員的內存地址;this
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); //根據unsafe的cas操做,返回true or false }
看一下上面API的實現:atom
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final int getAndDecrement() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return current; } } public final int getAndAdd(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return current; } } public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
方法實現整體差很少,for(;;)死循環,讀取當前值,根據相應的加減cas操做,成功從for中返回,失敗不一樣重試;spa
測試用例以下:.net
import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import static org.junit.Assert.*; public class AtomicIntegerTest { @Test public void testAll() throws InterruptedException { final AtomicInteger value = new AtomicInteger(10); assertEquals(value.compareAndSet(1, 2), false); assertEquals(value.get(), 10); assertTrue(value.compareAndSet(10, 3)); assertEquals(value.get(), 3); value.set(0); // assertEquals(value.incrementAndGet(), 1); assertEquals(value.getAndAdd(2), 1); assertEquals(value.getAndSet(5), 3); assertEquals(value.get(), 5); // final final int threadSize = 5; Thread[] threads = new Thread[threadSize]; for (int i = 0; i < threadSize; i++) { threads[i] = new Thread() { public void run() { value.incrementAndGet(); } }; } for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } // assertEquals(value.get(), 5 + threadSize); } }
原文來至:http://www.blogjava.net/xylz/archive/2010/07/08/325587.html