[Java多線程 七]---JUC包下原子類

主要內容引自大神的博客:http://www.cnblogs.com/skywang12345/p/3514589.html

分類

原子類的類結構API

這裏寫圖片描述

根據修改的數據類型,可以將JUC包中的原子操作類可以分爲4類。

  1. 基本類型: AtomicInteger, AtomicLong, AtomicBoolean ;

  2. 數組類型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ;

  3. 引用類型: AtomicReference, AtomicStampedRerence, AtomicMarkableReference ;

  4. 對象的屬性修改類型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 。

這些類存在的目的是對相應的數據進行原子操作。所謂原子操作,是指操作過程不會被中斷,保證數據操作是以原子方式進行的。

多個線程執行一個操作時,其中任何一個線程要麼完全執行完此操作,要麼沒有執行此操作的任何步驟,那麼這個操作就是原子的

出現原因: synchronized的代價比較高。

基本類型

AtomicInteger, AtomicLong和AtomicBoolean這3個基本類型的原子類的原理和用法相似。原子類並非不可變的,但它們應該是屬於相對安全的類別本章以AtomicLong對基本類型的原子類進行介紹。

AtomicLong基本介紹

AtomicLong是作用是對長整形進行原子操作。在32位操作系統中,64位的long 和 double 變量由於會被JVM當作兩個分離的32位來進行操作,所以不具有原子性。而使用AtomicLong能讓long的操作保持原子型。

AtomicLong函數列表

// 構造函數
AtomicLong()
// 創建值爲initialValue的AtomicLong對象
AtomicLong(long initialValue)
// 以原子方式設置當前值爲newValue。
final void set(long newValue) 
// 獲取當前值
final long get() 
// 以原子方式將當前值減 1,並返回減1後的值。等價於“--num”
final long decrementAndGet() 
// 以原子方式將當前值減 1,並返回減1前的值。等價於“num--”
final long getAndDecrement() 
// 以原子方式將當前值加 1,並返回加1後的值。等價於“++num”
final long incrementAndGet() 
// 以原子方式將當前值加 1,並返回加1前的值。等價於“num++”
final long getAndIncrement()    
// 以原子方式將delta與當前值相加,並返回相加後的值。
final long addAndGet(long delta) 
// 以原子方式將delta添加到當前值,並返回相加前的值。
final long getAndAdd(long delta) 
// 如果當前值 == expect,則以原子方式將該值設置爲update。成功返回true,否則返回false,並且不修改原值。
final boolean compareAndSet(long expect, long update)
// 以原子方式設置當前值爲newValue,並返回舊值。
final long getAndSet(long newValue)
// 返回當前值對應的int值
int intValue() 
// 獲取當前值對應的long值
long longValue()    
// 以 float 形式返回當前值
float floatValue()    
// 以 double 形式返回當前值
double doubleValue()    
// 最後設置爲給定值。延時設置變量值,這個等價於set()方法,但是由於字段是volatile類型的,因此次字段的修改會比普通字段(非volatile字段)有稍微的性能延時(儘管可以忽略),所以如果不是想立即讀取設置的新值,允許在“後臺”修改值,那麼此方法就很有用。如果還是難以理解,這裏就類似於啓動一個後臺線程如執行修改新值的任務,原線程就不等待修改結果立即返回(這種解釋其實是不正確的,但是可以這麼理解)。
final void lazySet(long newValue)
// 如果當前值 == 預期值,則以原子方式將該設置爲給定的更新值。JSR規範中說:以原子方式讀取和有條件地寫入變量但不 創建任何 happen-before 排序,因此不提供與除 weakCompareAndSet 目標外任何變量以前或後續讀取或寫入操作有關的任何保證。大意就是說調用weakCompareAndSet時並不能保證不存在happen-before的發生(也就是可能存在指令重排序導致此操作失敗)。但是從Java源碼來看,其實此方法並沒有實現JSR規範的要求,最後效果和compareAndSet是等效的,都調用了unsafe.compareAndSwapInt()完成操作。
final boolean weakCompareAndSet(long expect, long update)

AtomicLong源碼分析

AtomicLong的代碼很簡單,下面僅以incrementAndGet()爲例,對AtomicLong的原理進行說明。

public final long incrementAndGet() {
    for (;;) {
        // 獲取AtomicLong當前對應的long值
        long current = get();   //得到的是返回的舊值
        // 將current加1
        long next = current + 1;
        // 通過CAS函數,更新current的值
        if (compareAndSet(current, next))  //如果舊值和內存中的相等,則更新爲新值
            return next;
    }
}

(1) incrementAndGet()首先會根據get()獲取AtomicLong對應的long值。該值是volatile類型的變量,get()的源碼如下:

// value是AtomicLong對應的long值
private volatile long value;
// 返回AtomicLong對應的long值
public final long get() {
    return value;
}

(2) incrementAndGet()接着將current加1,然後通過CAS函數,將新的值賦值給value。compareAndSet()的源碼如下:

public final boolean compareAndSet(long expect, long update) {
    return unsafe.compareAndSwapLong(this, valueOffset, expect, update);  //無論是否更新值,都會返回舊值
}

compareAndSet()的作用是更新AtomicLong對應的long值。它會比較AtomicLong的原始值是否與expect相等,若相等的話,則設置AtomicLong的值爲update。

AtomicLong示例

//LongTest.java的源碼
import java.util.concurrent.atomic.AtomicLong;

public class LongTest {

    public static void main(String[] args){

        // 新建AtomicLong對象
        AtomicLong mAtoLong = new AtomicLong();

        mAtoLong.set(0x0123456789ABCDEFL);
        System.out.printf("%20s : 0x%016X\n", "get()", mAtoLong.get());
        System.out.printf("%20s : 0x%016X\n", "intValue()", mAtoLong.intValue());
        System.out.printf("%20s : 0x%016X\n", "longValue()", mAtoLong.longValue());
        System.out.printf("%20s : %s\n", "doubleValue()", mAtoLong.doubleValue());
        System.out.printf("%20s : %s\n", "floatValue()", mAtoLong.floatValue());

        System.out.printf("%20s : 0x%016X\n", "getAndDecrement()", mAtoLong.getAndDecrement());
        System.out.printf("%20s : 0x%016X\n", "decrementAndGet()", mAtoLong.decrementAndGet());
        System.out.printf("%20s : 0x%016X\n", "getAndIncrement()", mAtoLong.getAndIncrement());
        System.out.printf("%20s : 0x%016X\n", "incrementAndGet()", mAtoLong.incrementAndGet());

        System.out.printf("%20s : 0x%016X\n", "addAndGet(0x10)", mAtoLong.addAndGet(0x10));
        System.out.printf("%20s : 0x%016X\n", "getAndAdd(0x10)", mAtoLong.getAndAdd(0x10));

        System.out.printf("\n%20s : 0x%016X\n", "get()", mAtoLong.get());

        System.out.printf("%20s : %s\n", "compareAndSet()", mAtoLong.compareAndSet(0x12345679L, 0xFEDCBA9876543210L));
        System.out.printf("%20s : 0x%016X\n", "get()", mAtoLong.get());
    }
}

運行結果

 get() : 0x0123456789ABCDEF
          intValue() : 0x0000000089ABCDEF
         longValue() : 0x0123456789ABCDEF
       doubleValue() : 8.1985529216486896E16
        floatValue() : 8.1985531E16
   getAndDecrement() : 0x0123456789ABCDEF
   decrementAndGet() : 0x0123456789ABCDED
   getAndIncrement() : 0x0123456789ABCDED
   incrementAndGet() : 0x0123456789ABCDEF
     addAndGet(0x10) : 0x0123456789ABCDFF
     getAndAdd(0x10) : 0x0123456789ABCDFF

               get() : 0x0123456789ABCE0F
     compareAndSet() : false  //原始值不等於預期值,所以返回false
               get() : 0x0123456789ABCE0F

數組類型

AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray這3個數組類型的原子類的原理和用法相似。本章以AtomicLongArray對數組類型的原子類進行介紹。

AtomicLongArray基本介紹

AtomicLong是作用是對長整形進行原子操作。而AtomicLongArray的作用則是對”長整形數組”進行原子操作。

AtomicLongArray函數列表

// 創建給定長度的新 AtomicLongArray。
AtomicLongArray(int length)
// 創建與給定數組具有相同長度的新 AtomicLongArray,並從給定數組複製其所有元素。
AtomicLongArray(long[] array)

// 以原子方式將給定值添加到索引 i 的元素。
long addAndGet(int i, long delta)
// 如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。
boolean compareAndSet(int i, long expect, long update)
// 以原子方式將索引 i 的元素減1。
long decrementAndGet(int i)
// 獲取位置 i 的當前值。
long get(int i)
// 以原子方式將給定值與索引 i 的元素相加。
long getAndAdd(int i, long delta)
// 以原子方式將索引 i 的元素減 1。
long getAndDecrement(int i)
// 以原子方式將索引 i 的元素加 1。
long getAndIncrement(int i)
// 以原子方式將位置 i 的元素設置爲給定值,並返回舊值。
long getAndSet(int i, long newValue)
// 以原子方式將索引 i 的元素加1。
long incrementAndGet(int i)
// 最終將位置 i 的元素設置爲給定值。
void lazySet(int i, long newValue)
// 返回該數組的長度。
int length()
// 將位置 i 的元素設置爲給定值。
void set(int i, long newValue)
// 返回數組當前值的字符串表示形式。
String toString()
// 如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。
boolean    weakCompareAndSet(int i, long expect, long update)

AtomicLongArray的源碼分析

AtomicLongArray的代碼很簡單,下面僅以incrementAndGet()爲例,對AtomicLong的原理進行說明。incrementAndGet()源碼如下:

public final long incrementAndGet(int i) {
    return addAndGet(i, 1);
}

說明:incrementAndGet()的作用是以原子方式將long數組的索引 i 的元素加1,並返回加1之後的值。

public long addAndGet(int i, long delta) {
    // 檢查數組是否越界
    long offset = checkedByteOffset(i);
    while (true) {
        // 獲取long型數組的索引 offset 的原始值
        long current = getRaw(offset);
        // 修改long型值
        long next = current + delta;
        // 通過CAS更新long型數組的索引 offset的值。
        if (compareAndSetRaw(offset, current, next))
            return next;
    }
}

說明:addAndGet()首先檢查數組是否越界。如果沒有越界的話,則先獲取數組索引i的值;然後通過CAS函數更新i的值。

getRaw()源碼如下:

private long getRaw(long offset) {
    return unsafe.getLongVolatile(array, offset);
}

說明:unsafe是通過Unsafe.getUnsafe()返回的一個Unsafe對象。通過Unsafe的CAS函數對long型數組的元素進行原子操作。如compareAndSetRaw()就是調用Unsafe的CAS函數,它的源碼如下:

private boolean compareAndSetRaw(long offset, long expect, long update) {
    return unsafe.compareAndSwapLong(array, offset, expect, update);
}

AtomicLongArray示例

// LongArrayTest.java的源碼
import java.util.concurrent.atomic.AtomicLongArray;

public class LongArrayTest {

    public static void main(String[] args){

        // 新建AtomicLongArray對象
        long[] arrLong = new long[] {10, 20, 30, 40, 50};
        AtomicLongArray ala = new AtomicLongArray(arrLong);

        ala.set(0, 100);
        for (int i=0, len=ala.length(); i<len; i++) 
            System.out.printf("get(%d) : %s\n", i, ala.get(i));

        System.out.printf("%20s : %s\n", "getAndDecrement(0)", ala.getAndDecrement(0));
        System.out.printf("%20s : %s\n", "decrementAndGet(1)", ala.decrementAndGet(1));
        System.out.printf("%20s : %s\n", "getAndIncrement(2)", ala.getAndIncrement(2));
        System.out.printf("%20s : %s\n", "incrementAndGet(3)", ala.incrementAndGet(3));

        System.out.printf("%20s : %s\n", "addAndGet(100)", ala.addAndGet(0, 100));
        System.out.printf("%20s : %s\n", "getAndAdd(100)", ala.getAndAdd(1, 100));

        System.out.printf("%20s : %s\n", "compareAndSet()", ala.compareAndSet(2, 31, 1000));
        System.out.printf("%20s : %s\n", "get(2)", ala.get(2));
    }
}

運行結果

get(0) : 100
get(1) : 20
get(2) : 30
get(3) : 40
get(4) : 50
getAndDecrement(0) : 100
decrementAndGet(1) : 19
getAndIncrement(2) : 30
incrementAndGet(3) : 41
addAndGet(100) : 199
getAndAdd(100) : 19
compareAndSet() : true
get(2) : 1000

引用類型

對AtomicReference引用類型的原子類進行介紹

AtomicReference基本介紹

AtomicReference是作用是對”對象”進行原子操作。

AtomicReference函數列表

// 使用 null 初始值創建新的 AtomicReference。
AtomicReference()
// 使用給定的初始值創建新的 AtomicReference。
AtomicReference(V initialValue)
// 如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。
boolean compareAndSet(V expect, V update)
// 獲取當前值。
V get()
// 以原子方式設置爲給定值,並返回舊值。
V getAndSet(V newValue)
// 最終設置爲給定值。
void lazySet(V newValue)
// 設置爲給定值。
void set(V newValue)
// 返回當前值的字符串表示形式。
String toString()
// 如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。
boolean weakCompareAndSet(V expect, V update)

AtomicReference源碼分析

public class AtomicReference<V>  implements java.io.Serializable {
    private static final long serialVersionUID = -1848883965231344442L;

    // 獲取Unsafe對象,Unsafe的作用是提供CAS操作
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicReference.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

    // volatile類型
    private volatile V value;

    public AtomicReference(V initialValue) {
        value = initialValue;
    }

    public AtomicReference() {
    }

    public final V get() {
        return value;
    }

    public final void set(V newValue) {
        value = newValue;
    }

    public final void lazySet(V newValue) {
        unsafe.putOrderedObject(this, valueOffset, newValue);
    }

    public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

    public final V getAndSet(V newValue) {
        while (true) {
            V x = get();
            if (compareAndSet(x, newValue))
                return x;
        }
    }

    public String toString() {
        return String.valueOf(get());
    }
}

說明:AtomicReference的源碼比較簡單。它是通過”volatile”和”Unsafe提供的CAS函數實現”原子操作。

(01) value是volatile類型。這保證了:當某線程修改value的值時,其他線程看到的value值都是最新的value值,即修改之後的volatile的值

(02) 通過CAS設置value。這保證了:當某線程池通過CAS函數(如compareAndSet函數)設置value時,它的操作是原子的,即線程在操作value時不會被中斷。

AtomicReference示例

// AtomicReferenceTest.java的源碼
import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {

    public static void main(String[] args){

        // 創建兩個Person對象,它們的id分別是101和102。
        Person p1 = new Person(101);
        Person p2 = new Person(102);
        // 新建AtomicReference對象,初始化它的值爲p1對象
        AtomicReference ar = new AtomicReference(p1);
        // 通過CAS設置ar。如果ar的值爲p1的話,則將其設置爲p2。
        ar.compareAndSet(p1, p2);

        Person p3 = (Person)ar.get();
        System.out.println("p3 is "+p3);
        System.out.println("p3.equals(p1)="+p3.equals(p1));
        System.out.println("p3.equals(p2)="+p3.equals(p2));
    }
}

class Person {
    volatile long id;
    public Person(long id) {
        this.id = id;
    }
    public String toString() {
        return "id:"+id;
    }
}

運行結果

p3 is id:102
p3.equals(p1)=false
p3.equals(p2)=true(p2==p3也是返回true

結果說明
新建AtomicReference對象ar時,將它初始化爲p1。緊接着,通過CAS函數對它進行設置。如果ar的值爲p1的話,則將其設置爲p2。最後,獲取ar對應的對象,並打印結果。p3.equals(p1)的結果爲false,這是因爲Person並沒有覆蓋equals()方法,而是採用繼承自Object.java的equals()方法;而Object.java中的equals()實際上是調用”==”去比較兩個對象,即比較兩個對象的地址是否相等。

對象的屬性修改類型

AtomicIntegerFieldUpdater, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater這3個修改類的成員的原子類型的原理和用法相似。本章以對基本類型的原子類AtomicLongFieldUpdater進行介紹

AtomicLongFieldUpdater基本介紹

AtomicLongFieldUpdater可以對指定”類的 ‘volatile long’類型的成員”進行原子更新。它是基於反射原理實現的。

AtomicLongFieldUpdater函數列表

// 受保護的無操作構造方法,供子類使用。
protected AtomicLongFieldUpdater()

// 以原子方式將給定值添加到此更新器管理的給定對象的字段的當前值。
long addAndGet(T obj, long delta)
// 如果當前值 == 預期值,則以原子方式將此更新器所管理的給定對象的字段設置爲給定的更新值。
abstract boolean compareAndSet(T obj, long expect, long update)
// 以原子方式將此更新器管理的給定對象字段當前值減 1。
long decrementAndGet(T obj)
// 獲取此更新器管理的在給定對象的字段中保持的當前值。
abstract long get(T obj)
// 以原子方式將給定值添加到此更新器管理的給定對象的字段的當前值。
long getAndAdd(T obj, long delta)
// 以原子方式將此更新器管理的給定對象字段當前值減 1。
long getAndDecrement(T obj)
// 以原子方式將此更新器管理的給定對象字段的當前值加 1。
long getAndIncrement(T obj)
// 將此更新器管理的給定對象的字段以原子方式設置爲給定值,並返回舊值。
long getAndSet(T obj, long newValue)
// 以原子方式將此更新器管理的給定對象字段當前值加 1。
long incrementAndGet(T obj)
// 最後將此更新器管理的給定對象的字段設置爲給定更新值。
abstract void lazySet(T obj, long newValue)
// 爲對象創建並返回一個具有給定字段的更新器。
static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName)
// 將此更新器管理的給定對象的字段設置爲給定更新值。
abstract void set(T obj, long newValue)
// 如果當前值 == 預期值,則以原子方式將此更新器所管理的給定對象的字段設置爲給定的更新值。
abstract boolean weakCompareAndSet(T obj, long expect, long update)

AtomicLongFieldUpdater源碼分析

下面分析LongFieldTest.java的流程。
* newUpdater() , newUpdater()的源碼如下:*

public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
    Class<?> caller = Reflection.getCallerClass();
    if (AtomicLong.VM_SUPPORTS_LONG_CAS)
        return new CASUpdater<U>(tclass, fieldName, caller);
    else
        return new LockedUpdater<U>(tclass, fieldName, caller);
}

說明:newUpdater()的作用是獲取一個AtomicIntegerFieldUpdater類型的對象。它實際上返回的是CASUpdater對象,或者LockedUpdater對象;具體返回哪一個類取決於JVM是否支持long類型的CAS函數。CASUpdater和LockedUpdater都是AtomicIntegerFieldUpdater的子類,它們的實現類似。下面以CASUpdater來進行說明。

CASUpdater類的源碼如下



public boolean compareAndSet(T obj, long expect, long update) {
    if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
    return unsafe.compareAndSwapLong(obj, offset, expect, update);
}

說明:它實際上是通過CAS函數操作。如果類的long對象的值是expect,則設置它的值爲update。

AtomicLongFieldUpdater示例

// LongTest.java的源碼
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

public class LongFieldTest {

    public static void main(String[] args) {

        // 獲取Person的class對象
        Class cls = Person.class; 
        // 新建AtomicLongFieldUpdater對象,傳遞參數是“class對象”和“long類型在類中對應的名稱”
        AtomicLongFieldUpdater mAtoLong = AtomicLongFieldUpdater.newUpdater(cls, "id");
        Person person = new Person(12345678L);

        // 比較person的"id"屬性,如果id的值爲12345678L,則設置爲1000。
        mAtoLong.compareAndSet(person, 12345678L, 1000);
        System.out.println("id="+person.getId());
    }
}

class Person {
    volatile long id;
    public Person(long id) {
        this.id = id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public long getId() {
        return id;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章