天天看點

Java并發程式設計一引用類型、更新類型原子類初使用加源碼分析

推薦:​​Java并發程式設計彙總​​

Java并發程式設計一引用類型、更新類型原子類初使用加源碼分析

首先我們來看一看有哪些原子類。

Java并發程式設計一引用類型、更新類型原子類初使用加源碼分析
Java并發程式設計一引用類型、更新類型原子類初使用加源碼分析

現在我們來看看該如何去使用這些引用類型、更新類型原子類吧。

之前已經介紹過基本類型、數組類型原子類和累加器的使用了,講過的原理這裡就不會再涉及了,想了解就看下面這篇部落格吧。

​​Java并發程式設計一基本類型、數組類型原子類和累加器初使用加源碼分析​​

AtomicReference

​AtomicReference​

​類會将一個引用類型包裝成原子類,現在我們來看一看如何使用吧。

這裡我們使用​

​AtomicReference​

​​類來實作一個簡易版自旋鎖。

代碼:

package lock.spinlock;

import java.util.concurrent.atomic.AtomicReference;

public class SpinLock {

    private AtomicReference<Thread> sign = new AtomicReference<>();

    public void lock() {
        Thread current = Thread.currentThread();
        while (!sign.compareAndSet(null, current)) {
            System.out.println("自旋擷取失敗,再次嘗試");
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        sign.compareAndSet(current, null);
    }

    public static void main(String[] args) {
        SpinLock spinLock = new SpinLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "開始嘗試擷取自旋鎖");
                spinLock.lock();
                System.out.println(Thread.currentThread().getName() + "擷取到了自旋鎖");
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "釋放了自旋鎖");
                    spinLock.unlock();
                }
            }
        };
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
    }
}      

開頭輸出:

Thread-1開始嘗試擷取自旋鎖
Thread-0開始嘗試擷取自旋鎖
Thread-1擷取到了自旋鎖
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試      

結尾輸出:

自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
自旋擷取失敗,再次嘗試
Thread-0釋放了自旋鎖
自旋擷取失敗,再次嘗試
Thread-1擷取到了自旋鎖
Thread-1釋放了自旋鎖      

中間輸出還有很多的​

​自旋擷取失敗,再次嘗試​

​。

進入​

​AtomicReference​

​​類源碼,可以看到​

​AtomicReference​

​​類将引用類型包裝成原子類型的關鍵-​

​Unsafe​

​​工具類,​

​Unsafe​

​工具類在之前的那篇部落格已經介紹過了,這裡就不再贅述。

private static final Unsafe unsafe = Unsafe.getUnsafe();      

同樣使用反射來擷取要操作的屬性。

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

使用​

​volatile​

​​ 關鍵字修飾​

​value​

​​屬性,保證了​

​value​

​屬性的可見性,這和之前講過的原子類是一樣的。

private volatile V value;      

我們來看看​

​AtomicReference​

​類有哪些常用的方法。

/**
     * Gets the current value.
     *
     * @return the current value
     */
    public final V get() {
        return value;
    }      

​get()​

​​會擷取目前的​

​value​

​。

/**
     * Sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(V newValue) {
        value = newValue;
    }      

​set(V newValue)​

​​會進行指派操作,指派操作是原子操作,又因為用​

​volatile​

​​ 關鍵字修飾了​

​value​

​​屬性,保證了​

​value​

​屬性的可見性,這次指派操作,對其他線程是可見的。

/**
     * Atomically sets to the given value and returns the old value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    @SuppressWarnings("unchecked")
    public final V getAndSet(V newValue) {
        return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
    }      

​getAndSet(V newValue)​

​會先擷取舊值再賦新值,和之前介紹過的基本類型原子類一樣,這些方法都是原子操作,不然就會有線程安全的問題了。

就介紹這些常用的方法吧,其他的方法大家可以自己去看看源碼。

AtomicIntegerFieldUpdater

​AtomicIntegerFieldUpdater​

​​類,根據類名我們可能就已經知道它的作用了,它是将一個引用類型的​

​int(自動裝箱即可)或者Integer​

​類型的屬性更新為與原子類型。

我們來看一看它的用法吧。

代碼:

package atomic;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterDemo implements Runnable{

    static Candidate tom;
    static Candidate peter;

    public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
            .newUpdater(Candidate.class, "score");

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            peter.score++;
            scoreUpdater.getAndIncrement(tom);
        }
    }

    public static class Candidate {

        volatile int score;
    }

    public static void main(String[] args) throws InterruptedException {
        tom=new Candidate();
        peter=new Candidate();
        AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("普通變量:"+peter.score);
        System.out.println("更新後的結果"+ tom.score);
    }
}      

輸出:

普通變量:18860
更新後的結果20000      

很明顯,普通變量的​

​++​

​​操作存線上程安全問題,而經過使用​

​AtomicIntegerFieldUpdater​

​類進行更新的變量就沒有線程安全問題了(使用正确的情況下)。

從這段代碼可以很容易看出來,這裡也使用了反射。

public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
            .newUpdater(Candidate.class, "score");      
/**
     * Creates and returns an updater for objects with the given field.
     * The Class argument is needed to check that reflective types and
     * generic types match.
     *
     * @param tclass the class of the objects holding the field
     * @param fieldName the name of the field to be updated
     * @param <U> the type of instances of tclass
     * @return the updater
     * @throws IllegalArgumentException if the field is not a
     * volatile integer type
     * @throws RuntimeException with a nested reflection-based
     * exception if the class does not hold field or is the wrong type,
     * or the field is inaccessible to the caller according to Java language
     * access control
     */
    @CallerSensitive
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName) {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }      

使用​

​AtomicIntegerFieldUpdater​

​​類進行更新的變量其實就具有了和​

​AtomicInteger​

​類一樣的功能。

部分源碼:

public abstract int get(T obj);

    public int getAndSet(T obj, int newValue) {
        int prev;
        do {
            prev = get(obj);
        } while (!compareAndSet(obj, prev, newValue));
        return prev;
    }

    public int getAndIncrement(T obj) {
        int prev, next;
        do {
            prev = get(obj);
            next = prev + 1;
        } while (!compareAndSet(obj, prev, next));
        return prev;
    }

    public int getAndDecrement(T obj) {
        int prev, next;
        do {
            prev = get(obj);
            next = prev - 1;
        } while (!compareAndSet(obj, prev, next));
        return prev;
    }

    public int getAndAdd(T obj, int delta) {
        int prev, next;
        do {
            prev = get(obj);
            next = prev + delta;
        } while (!compareAndSet(obj, prev, next));
        return prev;
    }

    public int incrementAndGet(T obj) {
        int prev, next;
        do {
            prev = get(obj);
            next = prev + 1;
        } while (!compareAndSet(obj, prev, next));
        return next;
    }

    public int decrementAndGet(T obj) {
        int prev, next;
        do {
            prev = get(obj);
            next = prev - 1;
        } while (!compareAndSet(obj, prev, next));
        return next;
    }

    public int addAndGet(T obj, int delta) {
        int prev, next;
        do {
            prev = get(obj);
            next = prev + delta;
        } while (!compareAndSet(obj, prev, next));
        return next;
    }      

繼續閱讀