※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; } }
※