原子操作的封装类,它们位于java.util.concurrent.atomic
包。Atomic 类方法没有任何加锁,用了 volatile 修饰(消除ABA问题),保证了线程可见性进行CAS 原子操作,如果 CAS 失败会循环进行重试。
// 以原子方式递增给定的值,也就是将给定的值与现有值进行相加
public final int addAndGet(int delta)
// 原子更新,如果当前值等于预期值,则以原子方式将该值设置为输入的值(update)
public final boolean compareAndSet(int expect, int update)
// 以原子方式递增,将当前值加1,相当于i++,返回的是自增前的值
public final int getAndIncrement()
// 以原子方式递减,将当前值减1,相当于i--,返回的是递减前的值
public final int getAndDecrement()
// 以原子方式递增,相当于++i,返回的是自增后的值
public final int incrementAndGet()
// 以原子方式递减,相当于--1,返回的是递减后的值
public final int decrementAndGet()
// 返回当前值
public final int get()
// 设置当前值为给定的值
public final void set(int newValue)
// 顾名思义,设置并返回旧值
public final int getAndSet(int newValue)
// 添加给定值,并返回旧值
public final int getAndAdd(int delta)
// 添加给定值,返回新的值
public final int addAndGet(int delta)
// JDK8引入,设置并返回旧值,设置的值是使用一个函数式表达式的结果
public final int getAndUpdate(IntUnaryOperator updateFunction)
// 和上面一样,返回新的值
public final int updateAndGet(IntUnaryOperator updateFunction)
// 更新,返回旧的值,更新值为 当前值与给定值通过表达式进行计算的结果
public final int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction)
// 和上面类似,返回新的值
public final int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction)
// 设置值,以一种延迟的方式;设置值后,不保证该值的修改能被其他线程立刻看到;
// 也就是说,其他线程有可能读到的还是旧的值;
public final void lazySet(int newValue)
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(100);
// 以原子方式设置为给定值,返回旧值。
System.out.println(atomicInteger.getAndSet(2));
// 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值,如果成功返回true。
System.out.println(atomicInteger.compareAndSet(102,99));
// 以原子方式将给定值与当前值相加,返回新值。
System.out.println(atomicInteger.addAndGet(2));
// 以原子方式将给定值与当前值相加,返回旧值。
System.out.println(atomicInteger.getAndAdd(2));
// 以原子方式将当前值加1,返回新值。
System.out.println(atomicInteger.incrementAndGet());
// 以原子方式将当前值加1,返回旧值。
System.out.println(atomicInteger.getAndIncrement());
// 以原子方式将当前值减1,返回新值。
System.out.println(atomicInteger.decrementAndGet());
// 以原子方式将当前值减1,返回旧值。
System.out.println(atomicInteger.getAndDecrement());
}
public static void main(String[] args) {
AtomicReference reference = new AtomicReference();
// 设置为给定值。
reference.set("the string");
System.out.println(reference);
// 返回当前值。
System.out.println(reference.get());
// 以原子方式设置为给定值,返回旧值。
System.out.println(reference.getAndSet("new string"));
// 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值,如果成功返回true。
System.out.println(reference.compareAndSet("new string","last string"));
// 带有整数标志的AtomicReference
AtomicStampedReference stampedReference = new AtomicStampedReference("the string",123456);
// 返回该引用的当前值。
System.out.println(stampedReference.getReference());
// 返回该标志的当前值。
System.out.println(stampedReference.getStamp());
// 同时设置该引用和标志的值。
stampedReference.set("new string",654321);
//如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值,成功则返回true。
System.out.println(stampedReference.compareAndSet("new string","last string",654321,321456));
// 带有布尔标记的AtomicReference
AtomicMarkableReference markableReference = new AtomicMarkableReference("the string",true);
// 返回该引用的当前值。
System.out.println(markableReference.getReference());
// 返回该标志的当前值。
System.out.println(markableReference.getStamp());
// 同时设置该引用和标志的值。
markableReference.set("new string",false);
//如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值,成功则返回true。
System.out.println(markableReference.compareAndSet("new string","last string",false,true));
}
public static void main(String[] args) {
AtomicIntegerArray integerArray = new AtomicIntegerArray(10);
// 以原子方式将索引i的元素设置为给定值,返回旧值。
System.out.println(integerArray.getAndSet(0,2));
// 如果当前值 == 预期值,则以原子方式将索引i的元素设置为给定的更新值,如果成功返回true。
System.out.println(integerArray.compareAndSet(0,2,99));
// 以原子方式将给定值与索引i的元素相加,返回新值。
System.out.println(integerArray.addAndGet(0,2));
// 以原子方式将给定值与索引i的元素相加,返回旧值。
System.out.println(integerArray.getAndAdd(0,2));
// 以原子方式将索引i的元素加1,返回新值。
System.out.println(integerArray.incrementAndGet(0));
// 以原子方式将索引i的元素加1,返回旧值。
System.out.println(integerArray.getAndIncrement(0));
// 以原子方式将索引i的元素减1,返回新值。
System.out.println(integerArray.decrementAndGet(0));
// 以原子方式将索引i的元素减1,返回旧值。
System.out.println(integerArray.getAndDecrement(0));
// 引用类型数组原子类
AtomicReferenceArray referenceArray = new AtomicReferenceArray(10);
// 以原子方式将索引i的元素设置为给定值。
referenceArray.set(0,"the string");
// 返回索引i的元素的当前值。
System.out.println(referenceArray.get(0));
// 以原子方式将索引i的元素设置为给定值,返回旧值。
System.out.println(referenceArray.getAndSet(0,"new string"));
// 如果当前值 == 预期值,则以原子方式将索引i的元素设置为给定的更新值,成功则返回true。
System.out.println(referenceArray.compareAndSet(0,"new string","last string"));
}
public class AtomicDemo {
// volatile整形字段
public volatile int num = 100;
// volatile引用类型字段
public volatile String str = "the string";
public static void main(String[] args) {
AtomicDemo demo = new AtomicDemo();
// 使用给定字段为对象创建和返回一个整形字段更新器。
AtomicIntegerFieldUpdater integerUpdater = AtomicIntegerFieldUpdater.newUpdater(AtomicDemo.class,"num");
// 以原子方式将此更新器管理的给定对象的字段设置为给定值,返回旧值。
System.out.println(integerUpdater.getAndSet(demo,2));
// 如果当前值 == 预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值,如果成功返回true。
System.out.println(integerUpdater.compareAndSet(demo,102,99));
// 以原子方式将给定值与此更新器管理的给定对象的字段当前值相加,返回新值。
System.out.println(integerUpdater.addAndGet(demo,2));
// 以原子方式将给定值与此更新器管理的给定对象的字段当前值相加,返回旧值。
System.out.println(integerUpdater.getAndAdd(demo,2));
// 以原子方式将此更新器管理的给定对象的字段当前值加1,返回新值。
System.out.println(integerUpdater.incrementAndGet(demo));
// 以原子方式将此更新器管理的给定对象的字段当前值加1,返回旧值。
System.out.println(integerUpdater.getAndIncrement(demo));
// 以原子方式将此更新器管理的给定对象的字段当前值减1,返回新值。
System.out.println(integerUpdater.decrementAndGet(demo));
// 以原子方式将此更新器管理的给定对象的字段当前值减1,返回旧值。
System.out.println(integerUpdater.getAndDecrement(demo));
// 使用给定字段为对象创建和返回一个引用类型字段更新器。
AtomicReferenceFieldUpdater referenceUpdater = AtomicReferenceFieldUpdater.newUpdater(AtomicDemo.class, String.class, "str");
// 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值,如果成功返回true。
System.out.println(referenceUpdater.compareAndSet(demo,"the string","new string"));
}
}
在JDK8之前,针对原子操作,CAS+自旋操作适用于低并发(并发量高时自旋时间过长)。
java8中增加的原子类,基本思路就是分散热点,将 value 值分散到一个数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行 CAS 操作。要获取 long 值,要将各个槽中的变量值累加返回,其实是一种“空间换时间”的思想。
1.LongAccumulator
long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
2.LongAdder
long类型的累加器,LongAccumulator的特例,只能用来计算加法,且从0开始计算。
3.DoubleAccumulator
double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
4.DoubleAdder
double类型的累加器,DoubleAccumulator的特例,只能用来计算加法,且从0开始计算。
public static void main(String[] args) {
// 构造累加器
LongAdder adder = new LongAdder();
// 加上123
adder.add(123L);
// 加1
adder.increment();
// 减1
adder.decrement();
// 求和
adder.sum()();
// 重置
adder.reset();
// 构造累加器,指定自定义二元运算函数和初始值
LongAccumulator accumulator = new LongAccumulator((a,b)-> a + b, 0);
// 增加给定值
accumulator.accumulate(5);
// 返回当前值
accumulator.get();
// 重置累加器
accumulator.reset();
}