2019年5月16日 星期四

AtomicXxx、ABA

※AtomicInteger


AtomicInteger ai = new AtomicInteger(); // 建構子可給初始值,不給預設為 0
System.out.println(ai.get()); //取得當前值 0
System.out.println(ai.getAndIncrement()); // 後置遞增 0
System.out.println(ai.incrementAndGet()); // 前置遞增 2
System.out.println(ai.getAndAdd(3)); // 2
System.out.println(ai.addAndGet(3)); // 8
System.out.println(ai.getAndSet(7)); // 8
System.out.println(ai.get()); // 7
ai.set(9); // 回傳 void
System.out.println(ai.get()); // 9

※increment、decrement 為加減1,想加減更多用 addAndGet、getAndSet

※AtomicXxx Xxx 為 Wrapper 類別,都類似這樣的用法



※AtomicIntegerArray

int[] ints = {1, 2};
AtomicIntegerArray arr = new AtomicIntegerArray(ints);
arr.getAndSet(0, 9); // 第0個改成9
System.out.println(arr.get(0));//成功修改
System.out.println(ints[0]); // 原本的陣列並不會被改

※針對陣列的其中一個索引修改



※AtomicReference

MyClass c1 = new MyClass(1, "Tom");
MyClass c2 = new MyClass(2, "Jerry");
    
AtomicReference<MyClass> ref = new AtomicReference<>();
ref.set(c1);
ref.compareAndSet(c1, c2);
System.out.println(ref.get().getId());
System.out.println(ref.get().getName());
    
    
    
@Getter
@Setter
class MyClass {
    private int id;
    private String name;
    public MyClass(int id, String name){
        this.id = id;
        this.name = name;
    }
}

※針對整體物件的修改



※AtomicIntegerFieldUpdater

MyClass c1 = new MyClass(1, "Tom");
    
AtomicIntegerFieldUpdater<MyClass> atomic = AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "id");
System.out.println(atomic.getAndIncrement(c1));
System.out.println(atomic.get(c1));
    
class MyClass {
    volatile int id;
    private String name;
    MyClass(int id, String name){
        this.id = id;
        this.name = name;
    }
}

※針對物件裡的 Integer 修改,當然替換 Integer 也有其他 Wrapper 類別

※注意 Myclass 的欄位必需不是 private 且為 volatile 才可以,會有錯誤提示



※AtomicReferenceFieldUpdater

MyClass c1 = new MyClass(1, "Tom");
    
AtomicReferenceFieldUpdater<MyClass, String> atomic = AtomicReferenceFieldUpdater.newUpdater(MyClass.class, String.class, "name");
atomic.set(c1, "Jerry");
// System.out.println(atomic.getAndSet(c1, "Jerry"));
// System.out.println(atomic.compareAndSet(c1, "Tom", "Jerry")); // 舊值如果不是 Tom 就不更新,回傳 boolean
System.out.println(atomic.get(c1));
    
    
    
class MyClass {
    private int id;
    volatile String name;
    MyClass(int id, String name){
        this.id = id;
        this.name = name;
    }
}

※針對物件裡的物件修改

※使用此方式更新的欄位,一定要使用 Wrapper 類別,如此例 id 不是 Integer 會報錯,必需將 MyClass 和 newUpdate 的第二個參數都使用 Integer 才行



※AtomicStampedReference

Integer i1 = 10;
AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(i1, 200);
System.out.println(atomic.compareAndSet(10, 11, 200, 201)); // true,預期舊值是 10 且 stamp 是 200 就更新值為11,新的 stamp 為201
System.out.println(i1); // 10
System.out.println(atomic.getReference()); // 11
System.out.println(atomic.getStamp()); // 201
System.out.println(atomic.get(new int[]{i1})); // 11

※針對 ABA 的問題,用此類可解決

※前兩個參數 -128~127 因有 cache,看起來不會有問題,但超過就不行了,永遠是false,就算 i1 改用 int 也會自動裝箱成 Integer
Byte、Short、Integer、Long、Character 都有這個問題,可看 XxxCache
後兩個參數沒有這個問題,因為底層是 int


.較好的寫法

AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(1000, 200);
System.out.println(atomic.compareAndSet(atomic.getReference(), 11, 200, 201)); // true,以 atomic 為主就是同個記憶體,這樣就不會有問題了
System.out.println(atomic.getReference()); // 11
System.out.println(atomic.getStamp()); // 201

※這樣子寫就不用管 -128~127 的問題了


.使用 AtomicStampedReference 針對整個物件修改

MyClass c1 = new MyClass(1, "Tom");
MyClass c2 = new MyClass(2, "Jerry");
//Integer i1 = 10;
AtomicStampedReference<MyClass> atomic = new AtomicStampedReference<>(c1, 200);
System.out.println(atomic.compareAndSet(atomic.getReference(), c2, 200, 201)); // true
MyClass ref = atomic.getReference();
System.out.println(ref.getId()); // 2
System.out.println(ref.getName()); // Jerry
System.out.println(atomic.getStamp()); // 201
    
    
    
@Getter
@Setter
class MyClass {
    private int id;
    private String name;
    
    MyClass(int id, String name) {
        this.id = id;
        this.name = name;
    }
}



沒有留言:

張貼留言