原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中一部分,将整个操作视作一个整体是原子性的核心特征。
在java中提供了很多原子类,在此主要把这些原子类分成四大类。
原子更新基本类型或引用类型
如果是基本类型,则替换其值,如果是引用,则替换其引用地址,这些类主要有:
1、AtomicBoolean
原子更新布尔类型,内部使用int类型的value存储1和0表示true和false,底层也是对int类型的原子操作。
2、AtomicInteger
原子更新int类型。
3、AtomicLong
原子更新long类型。
4、AtomicReference
原子更新引用类型,通过泛型指定要操作的类。
5、AtomicMarkableReference
原子更新引用类型,内部使用Pair承载引用对象及是否被更新过的标记,避免了ABA问题。
6、AtomicStampedReference
原子更新引用类型,内部使用Pair承载引用对象及更新的邮戳,避免了ABA问题。
这几个类的操作基本类似,底层都调用Unsafe的compareAndSwapXxx()来实现,基本用法如下:
private static void testAtomicReference(){
AtomicInteger atomicInteger = new AtomicInteger(1);
atomicInteger.incrementAndGet();
atomicInteger.getAndIncrement();
atomicInteger.compareAndSet(3,666);
System.out.println(atomicInteger.get());
AtomicStampedReference atomicStampedReference = new AtomicStampedReference<>(1,1);
atomicStampedReference.compareAndSet(1,2,1,3);
atomicStampedReference.compareAndSet(2,666,3,5);
System.out.println(atomicStampedReference.getReference());
System.out.println(atomicStampedReference.getStamp());
}
原子更新数组中的元素
原子更新数组中的元素,可以更新数组中指定索引位置的元素,这些类主要有:
1、AtomicIntegerArray
原子更新int数组中的元素。
2、AtomicLongArray
原子更新long数组中的元素。
3、AtomicReferenceArray
原子更新Object数组中的元素。
这几个类的操作基本类似,更新元素时都要指定在数组中的索引位置,基本用法如下:
private static void testAtomicReferenceArray(){
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
atomicIntegerArray.getAndIncrement(O);
atomicIntegerArray.getAndAdd(1,666);
atomicIntegerArray.incrementAndGet(2);
atomicIntegerArray.addAndGet(3,666);
atomicIntegerArray.compareAndSet(4,0,666);
System.out.println(atomicIntegerArray.get(0));
System.out.println(atomicIntegerArray.get(1));
System.out.println(atomicIntegerArray.get(2));
System.out.println(atomicIntegerArray.get(3));
System.out.println(atomicIntegerArray.get(4));
System.out.println(atomicIntegerArray.get(5));
}
原子更新对象中的字段
原子更新对象中的字段,可以更新对象中指定字段名称的字段,这些类主要有:
1、AtomicIntegerFieldUpdater
原子更新对象中的int类型字段。
2、AtomicLongFieldUpdater
原子更新对象中的long类型字段。
3、AtomicReferenceFieldUpdater
原子更新对象中的引用类型字段。
这几个类的操作基本类似,都需要传入要更新的字段名称,基本用法如下:

private static void testAtomicReferenceField() {
AtomicReferenceFieldUpdater<User, String> updateName = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class,"name");
AtomicIntegerFieldUpdater updateAge = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

User user = new User("tong ge", 21);
updateName.compareAndSet(user, "tong ge", "read source code");
updateAge.compareAndSet(user, 21, 25);
updateAge.incrementAndGet(user);

System.out.println(user);

}

private static class User {
volatile String name;
volatile int age;

public User(String name, int age) {
    this.name = name;
    this.age = age;
}

@Override
public String toString() {
    return "name: " + name + ", age: " + age;
}

}
高性能原子类
高性能原子类,是java8中增加的原子类,它们使用分段的思想,把不同的线程hash到不同的段上去更新,最后再把这些段的值相加得到最终的值,这些类主要有:
1、Striped64
下面四个类的父类。
2、LongAccumulator
long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
3、LongAdder
long类型的累加器,LongAccumulator的特例,只能用来计算加法,且从0开始计算。
4、DoubleAccumulator
double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
5、DoubleAdder
double类型的累加器,DoubleAccumulator的特例,只能用来计算加法,且从0开始计算。
这几个类的操作基本类似,其中DoubleAccumulator和DoubleAdder底层其实也是用long来实现的,基本用法如下:
private static void testNewAtomic() {
LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.add(666);
System.out.println(longAdder.sum());

LongAccumulator longAccumulator = new LongAccumulator((left, right)->left + right * 2, 666);
longAccumulator.accumulate(1);
longAccumulator.accumulate(3);
longAccumulator.accumulate(-4);
System.out.println(longAccumulator.get());

}

Was this helpful?

0 / 0

发表回复 0

Your email address will not be published.