天天看點

Java并發Concurrent包的原子類原子變量類原子數組類原子更新類

Java 并發包 Concurrent 的包結構共可分為五個部分:

- 原子類

- 鎖

- collection并發集合架構

- excutor線程池

- 同步工具

本文介紹各種原子類。

主要有原子變量類,原子數組類,原子更新類。

原子變量類

原子變量類位于 java.util.concurrent.atomic 包下。

AtomicInteger 可以用原子方式更新的 int 值。

AtomicBoolean 可以用原子方式更新的 boolean 值。

AtomicLong 可以用原子方式更新的 long 值。

AtomicReference 可以用原子方式更新的對象引用。

以 AtomicInteger 為例,其它原子類的原理都一樣:

int get()

擷取目前值。

void set(int newValue)

設定為給定值。

void lazySet(int newValue)

最後設定為給定值。

int getAndSet(int newValue)

以原子方式設定為給定值,并傳回舊值。

這個相當于線程安全的下邊的代碼:

int temp = i;
    i = newValue;
    return temp;
           

boolean compareAndSet(int expect, int update)

如果目前值等于預期值,則以原子方式将該值設定為給定的更新值。

boolean weakCompareAndSet(int expect, int update)

如果目前值等于預期值,則以原子方式将該值設定為給定的更新值。

int getAndIncrement()

以原子方式将目前值加 1。傳回舊值。

相當于線程安全的 i++ 操作。

int getAndDecrement()

以原子方式将目前值減1。傳回舊值。

相當于線程安全的 i– 操作。

int getAndAdd(int delta)

以原子方式将給定值與目前值相加,傳回以前的舊值。

這個相當于線程安全的下邊的代碼:

int temp = i;
    i += delta;
    return temp;
           

int incrementAndGet()

以原子方式将目前值加 1。傳回新值。

相當于線程安全的 ++i 操作。

int decrementAndGet()

以原子方式将目前值減 1。傳回新值。

相當于線程安全的 –i 操作。

int addAndGet(int delta)

以原子方式将給定值與目前值相加,傳回新值。

這個相當于線程安全的 i += delta 操作。

測試,起 10萬 個線程,每個線程将變量自增一次,等所有線程執行完畢,然後檢視運作結果:

public class TestAtomic {

    private static AtomicInteger a = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        final int threadSize = ;
        Thread[] ts = new Thread[threadSize];
        for (int i = ; i < threadSize; i++) {
            ts[i] = new Thread() {
                public void run() {
                    a.incrementAndGet();
                }
            };
        }
        for(Thread t:ts) {
            t.start();
        }
        for(Thread t:ts) {
            t.join();
        }
        System.out.println(a);
    }

}
           

運作結果是:

100000
           

可以用普通的 int 類型來做一次比較,修改程式如下:

使用 a++ 操作。

public class TestAtomic {

    private static int a = ;

    public static void main(String[] args) throws InterruptedException {
        final int threadSize = ;
        Thread[] ts = new Thread[threadSize];
        for (int i = ; i < threadSize; i++) {
            ts[i] = new Thread() {
                public void run() {
                    a++;
                }
            };
        }
        for(Thread t:ts) {
            t.start();
        }
        for(Thread t:ts) {
            t.join();
        }
        System.out.println(a);
    }

}
           

運作結果,每次運作結果都不相同,但一般隻會比 10萬 小一些:

99991
           

原因就是 i++ 不是原子性的,産生了競态條件。

關于競态條件可以參考: Java中競态條件和使用synchronized關鍵字同步

http://blog.csdn.net/yx0628/article/details/79087110

原子數組類

以 AtomicIntegerArray 為例,其它的 AtomicLongArray , AtomicReferenceArray 都類似。

下面的方法,可以參考 AtomicInteger 的相應方法:http://blog.csdn.net/yx0628/article/details/79420960

int get(int i)

擷取位置 i 的目前值。

void set(int i, int newValue)

将位置 i 的元素設定為給定值。

void lazySet(int i, int newValue)

最後将位置 i 的元素設定為給定值。

int getAndSet(int i, int newValue)

将位置 i 的元素以原子方式設定為給定值,并傳回舊值。

boolean compareAndSet(int i, int expect, int update)

如果目前值等于預期值,則以原子方式将位置 i 的元素設定為給定的更新值。

boolean weakCompareAndSet(int i, int expect, int update)

如果目前值等于預期值,則以原子方式将位置 i 的元素設定為給定的更新值。

int getAndIncrement(int i)

以原子方式将索引 i 的元素加 1。

int getAndDecrement(int i)

以原子方式将索引 i 的元素減 1。

int getAndAdd(int i, int delta)

以原子方式将給定值與索引 i 的元素相加。

int incrementAndGet(int i)

以原子方式将索引 i 的元素加 1。

int decrementAndGet(int i)

以原子方式将索引 i 的元素減 1。

int addAndGet(int i, int delta)

以原子方式将給定值與索引 i 的元素相加。

原子更新類

原子更新類和原子類的方法基本類似,不過是基于反射來更新的。

AtomicIntegerFieldUpdater

基于反射的實用工具,可以對指定類的指定 volatile int 字段進行原子更新。

AtomicLongFieldUpdater

基于反射的實用工具,可以對指定類的指定 volatile long 字段進行原子更新。

AtomicReferenceFieldUpdater

基于反射的實用工具,可以對指定類的指定 volatile 字段進行原子更新。

更新類的要求:

- 字段必須是 volatile 類型, 不能是 static 或者 final 類型;

- 調用者能夠直接操作對象字段,那麼就可以反射進行原子操作。但是對于父類的字段,子類是不能直接操作的,盡管子類可以通路父類的字段;

- AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 隻能修改 int/long 類型的字段,不能修改其包裝類型(Integer/Long)。如果要修改包裝類型就需要使用AtomicReferenceFieldUpdater。

附上成員修飾符通路範圍:

Java并發Concurrent包的原子類原子變量類原子數組類原子更新類

下面以 AtomicReferenceFieldUpdater 的方法為例來看:

boolean compareAndSet(T obj, int expect, int update)

如果目前值等于預期值,則以原子方式将此更新器管理的給定對象的字段設定為給定的更新值。

boolean weakCompareAndSet(T obj, int expect, int update)

如果目前值等于預期值,則以原子方式将此更新器管理的給定對象的字段設定為給定的更新值。

void set(T obj, int newValue)

将此更新器管理的給定對象的字段設定為給定更新值。

void lazySet(T obj, int newValue)

最終将此更新器管理的給定對象的字段設定為給定更新值。

int get(T obj)

擷取由此更新器管理的在給定對象的字段中保持的目前值。

int getAndSet(T obj, int newValue)

将此更新器管理的給定對象的字段自動設定為給定值,并傳回舊值。

AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName)

使用給定的字段為對象建立和傳回一個更新器。

一個更新器的例子

Father.java

public class Father {
    public volatile int fatherValue = ;
}
           

Son.java

public class Son extends Father{
    public volatile int publicValue = ;
    volatile int defaultValue = ;
    protected volatile int protectedValue = ;      
    private volatile int privateValue = ;
}
           

Test類:

public class TestAtomic {

    public static void main(String[] args) {
        TestAtomic test = new TestAtomic();
        test.test();
    }

    private AtomicIntegerFieldUpdater<Son> getUpdater(String fieldname){
        return AtomicIntegerFieldUpdater.newUpdater(Son.class, fieldname);
    }

    private void test() {
        Son son = new Son();
        System.out.println(getUpdater("publicValue").getAndAdd(son, ));
        System.out.println(getUpdater("defaultValue").incrementAndGet(son));
        System.out.println(getUpdater("protectedValue").decrementAndGet(son));
        System.out.println(getUpdater("privateValue").compareAndSet(son, , ));
        // 父類的public
        System.out.println(getUpdater("fatherValue").getAndAdd(son, ));
    }
}
           

如果是這樣的包結構:

Java并發Concurrent包的原子類原子變量類原子數組類原子更新類

運作結果:

publicValue : 可以通路。

defaultValue : 報錯。原因:java.lang.IllegalAccessException 外部包 TestAtomic 類不能通路 Son 中的變量。

protectedValue : 報錯。原因:java.lang.IllegalAccessException 外部包 TestAtomic 類不能通路 Son 中的變量。

privateValue:報錯。原因:java.lang.IllegalAccessException 外部包 TestAtomic 類不能通路 Son 中的變量。

fatherValue : 報錯。原因:java.lang.NoSuchFieldException: fatherValue 父變量通路不到。

那麼如果把包結構改成這樣的:

Java并發Concurrent包的原子類原子變量類原子數組類原子更新類

運作結果:

publicValue : 可以通路。

defaultValue : 可以通路,本包可以通路。

protectedValue : 可以通路,本包可以通路。

privateValue:報錯。原因:java.lang.IllegalAccessException 外部包 TestAtomic 類不能通路 Son 中的變量。

fatherValue : 報錯。原因:java.lang.NoSuchFieldException: fatherValue 父變量通路不到。

通過這個例子可以說明原子更新類的變量通路限制。