1. 程式人生 > >JAVA 原子操作類

JAVA 原子操作類

上文中,guava程式碼中就用到了,在這裡再專門捋一下

部分內容源自:

https://www.jianshu.com/p/712681f5aecd

https://www.yiibai.com/java_concurrency/concurrency_atomiclong.html

AtomicReferenceArray<ReferenceEntry<K, V>>
根據變數型別的不同,Atomic包中的這12個原子操作類可以分為4種類型:
①原子更新基本型別AtomicBoolean、AtomicInteger、AtomicLong

②原子更新陣列AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
③原子更新引用AtomicReference、AtomicReferenceFiledUpdater、AtomicMarkableReference
④原子更新欄位(屬性)AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference
它們都是使用Unsafe實現的包裝類。   對於原子更新類基本操作,可以看到AtomicInteger中基本有以下一些方法,其他也都差不多,用法簡單
        Executor executor = Executors.newFixedThreadPool(3);
        AtomicInteger atomicInteger 
= new AtomicInteger(0); for(int i = 0; i < 10; i++){ executor.execute(()->{ System.out.println("atomicInteger的當前值:" + atomicInteger.addAndGet(1)); }); }

原子更新陣列,例AtomicReferenceArray,用法抄自上文,實際就是基於cas的操作

public class MainTest {
    
private static String[] source = new String[10]; private static AtomicReferenceArray<String> atomicReferenceArray = new AtomicReferenceArray<String>(source); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < atomicReferenceArray.length(); i++) { atomicReferenceArray.set(i, "item-2"); } Thread t1 = new Thread(new Increment()); Thread t2 = new Thread(new Compare()); t1.start(); t2.start(); t1.join(); t2.join(); } static class Increment implements Runnable { public void run() { for (int i = 0; i < atomicReferenceArray.length(); i++) { String add = atomicReferenceArray.getAndSet(i, "item-" + (i + 1)); System.out.println("Thread " + Thread.currentThread().getId() + ", index " + i + ", value: " + add); } } } static class Compare implements Runnable { public void run() { for (int i = 0; i < atomicReferenceArray.length(); i++) { System.out.println("Thread " + Thread.currentThread().getId() + ", index " + i + ", value: " + atomicReferenceArray.get(i)); boolean swapped = atomicReferenceArray.compareAndSet(i, "item-2", "updated-item-2"); System.out.println("Item swapped: " + swapped); if (swapped) { System.out .println("Thread " + Thread.currentThread().getId() + ", index " + i + ", updated-item-2"); } } } } }

AtomicReferenceFiledUpdater https://github.com/aCoder2013/blog/issues/10 這個blog寫的真不錯,在jdk中有很多應用。比如對buf的更新等

一個基於反射的工具類,它能對指定類的指定的volatile引用欄位進行原子更新。(注意這個欄位不能是private的) 

 class Node {
   private volatile Node left, right;

   private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater =
     AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
   private static AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
     AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");

   Node getLeft() { return left; }
   boolean compareAndSetLeft(Node expect, Node update) {
     return leftUpdater.compareAndSet(this, expect, update);
   }
   // ... and so on
 }}
public
class BufferedInputStream extends FilterInputStream {

    protected volatile byte buf[];

    /*
    *  原子的更新內部陣列,比如擴容、關閉時,
    */
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");


    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            //放在一個迴圈中,如果CAS更新失敗,那麼就讀取最新的buf引用,繼續CAS更新
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
        }
    }
}

AtomicStampedReference 通過包裝[E,Integer]的元組來對物件標記版本戳stamp,從而避免ABA問題