天天看點

【java并發】原子性操作類的使用

我的個人部落格站點:http://www.itcodai.com

  在java5以後,我們接觸到了線程原子性操作,也就是在修改時我們隻需要保證它的那個瞬間是安全的即可,經過相應的包裝後可以再處理對象的并發修改,本文總結一下Atomic系列的類的使用方法,其中包含:

類型 Integer Long
基本類型 AtomicInteger AtomicLong AtomicBoolean
數組類型 AtomicIntegerArray AtomicLongArray AtomicReferenceArray
屬性原子修改器 AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater

1. 基本類型的使用

  首先看一下AtomicInteger的使用,AtomicInteger主要是針對整數的修改的,看一下示例代碼:

public class AtomicIntegerDemo {

    /**
     * 常見的方法清單
     * @see AtomicInteger#get()             直接傳回值
     * @see AtomicInteger#getAndAdd(int)    增加指定的資料,傳回變化前的資料
     * @see AtomicInteger#getAndDecrement() 減少1,傳回減少前的資料
     * @see AtomicInteger#getAndIncrement() 增加1,傳回增加前的資料
     * @see AtomicInteger#getAndSet(int)    設定指定的資料,傳回設定前的資料
     * 
     * @see AtomicInteger#addAndGet(int)    增加指定的資料後傳回增加後的資料
     * @see AtomicInteger#decrementAndGet() 減少1,傳回減少後的值
     * @see AtomicInteger#incrementAndGet() 增加1,傳回增加後的值
     * @see AtomicInteger#lazySet(int)      僅僅當get時才會set
     * 
     * @see AtomicInteger#compareAndSet(int, int) 嘗試新增後對比,若增加成功則傳回true否則傳回false
     */
    public final static AtomicInteger TEST_INTEGER = new AtomicInteger();

    public static void main(String []args) {

         for(int i =  ; i <  ; i++) { //開啟10個線程

             new Thread() {
                 public void run() {
                     try {
                        Thread.sleep();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    int now = TEST_INTEGER.incrementAndGet(); //自增
                    System.out.println(Thread.currentThread().getName() + " get value : " + now);
                 }
             }.start();
         }
    }
}
           

  看一下結果:

Thread-3 get value : 4

Thread-7 get value : 5

Thread-9 get value : 9

Thread-4 get value : 6

Thread-0 get value : 3

Thread-1 get value : 8

Thread-5 get value : 11

Thread-8 get value : 7

Thread-2 get value : 10

Thread-6 get value : 2

  可以看出,10個線程之間是線程安全的,并沒有沖突。也就是說,我們使用原子性操作類去操作基本類型int就可以解決線程安全問題,一個線程在操作的時候,會對其它線程進行排斥,不用我們手動去使用synchronized實作互斥操作了。AtomicLong和AtomicBoolean類似,就不舉例子了。

2. 數組類型的使用

  下面要開始說Atomic的數組用法,Atomic的數組要求不允許修改長度等,不像集合類那麼豐富的操作,不過它可以讓數組上每個元素的操作絕對安全的,也就是它細化的力度還是到數組上的元素,做了二次包裝,雖然是數組類型的,但是最後還是操作數組中存的數,是以會了上面的基本類型的話,數組類型也很好了解。這裡主要看一下AtomicIntegerArray的使用,其它的類似。

public class AtomicIntegerArrayTest {

    /**
     * 常見的方法清單
     * @see AtomicIntegerArray#addAndGet(int, int) 執行加法,第一個參數為數組的下标,第二個參數為增加的數量,傳回增加後的結果
     * @see AtomicIntegerArray#compareAndSet(int, int, int) 對比修改,參數1:數組下标,參數2:原始值,參數3,修改目标值,修改成功傳回true否則false
     * @see AtomicIntegerArray#decrementAndGet(int) 參數為數組下标,将數組對應數字減少1,傳回減少後的資料
     * @see AtomicIntegerArray#incrementAndGet(int) 參數為數組下标,将數組對應數字增加1,傳回增加後的資料
     * 
     * @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet類似,差別是傳回值是變化前的資料
     * @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet類似,差別是傳回變化前的資料
     * @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet類似,差別是傳回變化前的資料
     * @see AtomicIntegerArray#getAndSet(int, int) 将對應下标的數字設定為指定值,第二個參數為設定的值,傳回是變化前的資料
     */
    private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{,,,,,,,,,});

    public static void main(String []args) throws InterruptedException {
        Thread []threads = new Thread[];
        for(int i =  ; i <  ; i++) {
            final int index = i;
            final int threadNum = i;
            threads[i] = new Thread() {
                public void run() {
                    int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + );
                    System.out.println("線程編号為:" + threadNum + " , 對應的原始值為:" + (index + ) + ",增加後的結果為:" + result);
                }
            };
            threads[i].start();
        }
        for(Thread thread : threads) {
            thread.join();
        }
        System.out.println("=========================>\n執行已經完成,結果清單:");
        for(int i =  ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
            System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
        }
    }
}
           

  運作結果是給每個數組元素加上相同的值,它們之間互不影響。

3. 作為類屬性的使用

  當某個資料類型是某個類中的一個屬性的時候,然後我們要操作該資料,就需要使用屬性原子修改器了,這裡還是以Integer為例,即:AtomicIntegerFieldUpdater。示例代碼如下:

public class AtomicIntegerFieldUpdaterTest {  

    static class A {  
        volatile int intValue = ;  
    }  

    /** 
     * 可以直接通路對應的變量,進行修改和處理 
     * 條件:要在可通路的區域内,如果是private或挎包通路default類型以及非父親類的protected均無法通路到 
     * 其次通路對象不能是static類型的變量(因為在計算屬性的偏移量的時候無法計算),也不能是final類型的變量(因為根本無法修改),必須是普通的成員變量 
     *  
     * 方法(說明上和AtomicInteger幾乎一緻,唯一的差別是第一個參數需要傳入對象的引用) 
     * @see AtomicIntegerFieldUpdater#addAndGet(Object, int) 
     * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int) 
     * @see AtomicIntegerFieldUpdater#decrementAndGet(Object) 
     * @see AtomicIntegerFieldUpdater#incrementAndGet(Object) 
     *  
     * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int) 
     * @see AtomicIntegerFieldUpdater#getAndDecrement(Object) 
     * @see AtomicIntegerFieldUpdater#getAndIncrement(Object) 
     * @see AtomicIntegerFieldUpdater#getAndSet(Object, int) 
     */  
    public final static AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");  

    public static void main(String []args) {  
        final A a = new A();  
        for(int i =  ; i <  ; i++) {  

            new Thread() {  
                public void run() {  
                    if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, , )) {  
                        System.out.println(Thread.currentThread().getName() + " 對對應的值做了修改!");  
                    }  
                }  
            }.start();  
        }  
    }  
}  
           

  可以看到,這裡需要将類和類屬性傳進去才行,傳進去後其實跟前面操作Integer沒什麼不同了,本質都一樣的,運作一下,結果隻有一個線程能對其進行修改。

  線程的原子性操作類的使用就簡單總結到這,其他的操作類原理都相似,可以參考JDK的文檔,可以很容易寫出相應的測試代碼。

  

  相關閱讀:http://blog.csdn.net/column/details/bingfa.html

文末福利:“程式員私房菜”,一個有溫度的公衆号~

【java并發】原子性操作類的使用

—–樂于分享,共同進步!

—–更多文章請看:http://blog.csdn.net/eson_15