《Java併發編程的藝術》第七章——Java中的13個原子操作類

當一個線程更新一個變量時,程序如果沒有正確的同步,那麼這個變量對於其他線程來說是不可見的。我們通常使用synchronized或者volatile來保證線程安全的更新共享變量。在JDK1.5中,提供了java.util.concurrent.atomic包,這個包中的原子操作類提供了一種用法簡單,性能高效,線程安全地更新一個變量的方式。
Atomic包裏一共提供了13個類,有4種類型的原子更新方式:原子更新基本類型、原子更新數組、原子更新引用和原子更新屬性。其實現基本都是使用Unsafe實現的包裝類。
1.原子更新基本類型類
- AtomicBoolean:原子更新布爾類型
- AtomicInteger:原子更新整型
- AtomicLong:原子更新長整型
以上3個類提供的方法基本一直,我們以AtomicInteger爲例進行分析。
AtomicInteger常用的方法有:
- int addAndGet(int delta):以原子方式將輸入的數值與實例中的值相加,並返回結果。
- boolean compareAndSet(int expect,int upate):如果輸入的數值等於預期值,則以原子方式將該值設置爲輸入的值。
- int getAndIncrement():以原子方式將當前值加1,返回自增前的值。
- void lazySet(int newValue):最終會設置成new Value,但可能導致其他線程在之後的一小段時間內還是可以讀到舊的值。
- int getAndSet(int newValue):以原子方式設置爲newValue,並返回舊值。


實例一:

package com.lipeng.seventh;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 * 多線程更新atomicIntegerDemo
 * @author LiPeng
 *
 */
public class AtomicIntegerDemo {
    static AtomicInteger atomicInteger=new AtomicInteger();
    public static void main(String[] args) {
        Thread thread1=new Thread(new TestAction(atomicInteger));
        Thread thread2=new Thread(new TestAction(atomicInteger));
        thread1.start();
        thread2.start();
    }

}
class TestAction implements Runnable{
    AtomicInteger atomicInteger;

    public TestAction(AtomicInteger atomicInteger) {
        super();
        this.atomicInteger = atomicInteger;
    }

    @Override
    public void run() {
        for(int i=0;i<10;++i){
            System.out.println(Thread.currentThread().getName()+" exec incrementAndGet menthod[i="+i+"] and new value:"+atomicInteger.getAndIncrement());
        }
    }
}

其實現依靠我們熟悉的CAS算法:
這裏寫圖片描述
在Java的基本類型中除了Atomic包中提供原子更新的基本類型外,還由char、float和double。那麼這些在Atomic包中沒有提供原子更新的基本類型怎麼保證其原子更新呢?
從AtomicBoolean源碼中我們可以得到答案:首先將Boolean轉換爲整型,然後使用comareAndSwapInt進行CAS,所以原子更新char、float、double同樣可以以此實現。
原子更新數組
- AtomicIntegerArray:原子更新整型數組裏的元素。
- AtomicLongArray:原子更新長整型數組裏的元素。
- AtomicReferenceArray:原子更新引用類型數組裏的元素。
【備註:】看書上說原子更新數組有4個類,除了上述3個外,還有AtomicBooleanArray類,但我在jdk5/6/7/8中都沒有找到這個類的存在,只找到共12個原子操作類,而不是標題中的13個。不知道是否是書中的錯誤?請知情的童鞋不吝賜教。
AtomicIntegerArray類主要提供原子的方式更新數組裏的整型,其常用方法如下:
- int addAndGet(int i,int delta):以原子方式將輸入值與數組中索引i的元素相加。
- boolean compareAndSet(int i,int expect,int update):如果當前值等於預期值,就把索引i的元素設置成update值。
【備註】:在AtomicIntegerArray構造方法中,AtomicIntegerArray會將數組複製一份,所以當其對內數組元素進行修改時,不會影響原傳入數組。
原子更新引用類型
原子更新基本類型的AtomicInteger,只能更新一個變量,如果需要原子更新多個變量,就需要使用這個原子更新引用類型提供的類。Atomic包提供以下3個類:
- AtomicReference:原子更新引用變量。
- AtomicReferenceFieldUpdater:原子更新引用類型裏的字段。
- AtomicMarkableReference:原子更新帶有標記位的引用類型 。可以原子更新一個布爾類型的標記位和引用類型。構造方法時AtomicMarkableReference(V initialRef,boolean initialMark)。


實例一:

package com.lipeng.seventh;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
 * AtomicReferenceDemo
 * @author LiPeng
 *
 */
public class AtomicReferenceDemo {
    static AtomicReference<User> atomicReference=new AtomicReference<User>();
    public static void main(String[] args) {
        User user=new User("1","zhangsan");
        //將user設置進去
        atomicReference.set(user);
        User newUser=new User("2","lisi");
        //cas更新atomicReference裏user的引用
        atomicReference.compareAndSet(user, newUser);
        System.out.println(atomicReference.get().toString());
    }

    static class User{
        private String userId;
        private String userName;
        public String getUserId() {
            return userId;
        }
        public void setUserId(String userId) {
            this.userId = userId;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public User(String userId, String userName) {
            super();
            this.userId = userId;
            this.userName = userName;
        }
        @Override
        public String toString() {
            return "User [userId=" + userId + ", userName=" + userName + "]";
        }

    }
}

實例二:

package com.lipeng.seventh;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
 * AtomicReferenceFieldUpdaterDemo
 * 原子更新User類中userName字段,此字段必須定義爲public且volatile
 * @author LiPeng
 *
 */
public class AtomicReferenceFieldUpdaterDemo {
    static AtomicReferenceFieldUpdater<User, String> atomicFiled=AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "userName");
    public static void main(String[] args) {
        User user=new User("1","zhangsan");
        atomicFiled.compareAndSet(user, user.userName, "lisi");
        System.out.println(atomicFiled.get(user));
    }
    static class User{
        public String userId;
        public volatile String userName;
        public String getUserId() {
            return userId;
        }
        public void setUserId(String userId) {
            this.userId = userId;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public User(String userId, String userName) {
            super();
            this.userId = userId;
            this.userName = userName;
        }
        @Override
        public String toString() {
            return "User [userId=" + userId + ", userName=" + userName + "]";
        }

    }
}

原子更新字段類
如果需要原子的更新某個類裏的某個字段,就需要使用原子更新字段類,Atomic包提供了以下3個類進行原子字段更新:
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFiledUpdater:原子更新長整型字段的更新器。
- AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,用於原子的更新數據和數據的版本號,避免CAS的ABA問題、
想要原子的更新字段類需要調用靜態方法newUpdater()創建一個更新器,並設置想要更新的類和屬性。且更新類的字段必須使用public volatile修飾符。


實例一:

package com.lipeng.seventh;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
 * AtomicIntegerFieldUpdaterDemo
 * 原子更新user中age字段
 * @author LiPeng
 *
 */
public class AtomicIntegerFieldUpdaterDemo {
    static AtomicIntegerFieldUpdater<User> atomicFiledUpdater=AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
    public static void main(String[] args) {
        User user=new User("zhangsan",24);
        //age 自增一歲
        atomicFiledUpdater.incrementAndGet(user);
        System.out.println(atomicFiledUpdater.get(user));
    }


    static class User{
        public String name;
        public volatile int age;
        public User(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章