Java并发编程之原子类(一)

75 阅读3分钟

JUC中的原子类主要有以下几类:

  • 原子整数
  • 原子引用
  • 原子数组
  • 原子更新器
  • 原子累加器

原子整数

原子整数主要有AtomicBooleanAtomicIntegerAtomicLong,三个类中都是通过value属性来存储值,其中AtomicIntegerAtomicLong方法几乎一样,所以这里只介绍一个就好了。

private volatile int value;
AtomicBoolean

两个构造方法,无参构造方法value值就是默认值0,有参构造方法value值就是传入的参数。

public AtomicBoolean() {
}
public AtomicBoolean(int initialValue) {
    value = initialValue;
}

get方法获取当前值,因为valueint类型,所以返回的是value!=0

public final boolean get() {
    return value != 0;
}

获取旧值并设置新值,这是一个原子方法,保证了获取并设置新值的过程不会被打断

public final boolean getAndSet(boolean newValue)
AtomicInteger/AtomicLong

AtomicIntegerAtomicLong方法一样,只是类型一个是int一个是long而已

同样地两个构造方法,无参构造方法value值就是默认值0,有参构造方法value值就是传入的参数。

public AtomicInteger() {
}
public AtomicInteger(int initialValue) {
    value = initialValue;
}

获取旧值并自增1,等同于i++

public final int getAndIncrement()

获取旧值并自减1,等同于i--

public final int getAndDecrement()

先自增1再获取新值,等同于++i

public final int incrementAndGet()

先自减1再获取新值,等同于--i

public final int decrementAndGet()

先加delta再获取新值

public final int addAndGet(int delta)

先获取旧值再加delta

public final int getAndAdd(int delta)

先获取旧值,再根据指定方法进行更新,不局限于加减法,可以是任何复杂计算

public final int getAndUpdate(IntUnaryOperator updateFunction)

// 对i乘以10
AtomicInteger i = new AtomicInteger(1);
i.getAndUpdate(v -> v * 10)

先根据指定方法进行更新,再获取新值,使用方法同getAndUpdate

public final int updateAndGet(IntUnaryOperator updateFunction)

原子引用

原子引用主要有AtomicReferenceAtomicStampedReferenceAtomicMarkableReference三个类。

AtomicReference

通过一个泛型类型value来存储值,所以可以是任意引用类型,调用有参构造方法传入初始值,调用无参构造初始值就是null

    private volatile V value;

    public AtomicReference(V initialValue) {
        value = initialValue;
    }
   
    public AtomicReference() {
    }

get方法获取当前值

public final V get()

先获取旧值再设置新值

public final V getAndSet(V newValue)

先获取旧值,然后通过一个函数式接口中的方法结果设置新值

public final V getAndUpdate(UnaryOperator<V> updateFunction)

先执行操作来更新值,然后再获取新值

public final V updateAndGet(UnaryOperator<V> updateFunction)
AtomicStampedReference

上一篇文章 Java并发编程之CAS原理 中提到ABA问题的解决方法,就可以通过AtomicStampedReference来实现

通过内部类Pair来存储两个值,一个是引用的值reference,一个是int类型版本号stamp,每次修改都可以设置不同的值

public class AtomicStampedReference<V> {
    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }
}

构造方法对引用值和版本号进行初始化

public AtomicStampedReference(V initialRef, int initialStamp) {...}

比较旧值和期待的值是否相等,并且比较版本号和期待的版本号是否相等,两个都相等才会设置新值和新版本号

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp)
AtomicMarkableReference

如果在更新引用时并不关心中间被修改过多少次,只关心是否被更改过,那么只需要设置一个标记就可以了,AtomicMarkableReference就可以解决这种情况。

同样也是通过一个内部类来存储两个值,不过这里存储的是引用值和boolean类型的标记

public class AtomicMarkableReference<V> {

    private static class Pair<T> {
        final T reference;
        final boolean mark;
        ...
    }
    ...
}

构造方法对引用值和标记值进行初始化

public AtomicMarkableReference(V initialRef, boolean initialMark) {...}

比较旧值和期待的值是否相等,并且比较旧的标记和期待的标记是否相等,两个都相等才会设置新值和新标记

public boolean compareAndSet(V       expectedReference,
                             V       newReference,
                             boolean expectedMark,
                             boolean newMark)