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

这些方法和AtomicInteger类中的方法差不多,就不多讲了,可以参考下面这篇博客。

Java并发编程一基本类型、数组类型原子类和累加器初使用加源码分析

发布了309 篇原创文章 · 获赞 448 · 访问量 71万+

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/104373102