原子操做是指不会被线程调度机制打断的操做,这种操做一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。java
原子操做能够是一个步骤,也能够是多个操做步骤,可是其顺序不能够被打乱,也不能够被切割而只执行其中的一部分,将整个操做视做一个总体是原子性的核心特征。git
在java中提供了不少原子类,笔者在此主要把这些原子类分红四大类。数据库
若是是基本类型,则替换其值,若是是引用,则替换其引用地址,这些类主要有:数组
(1)AtomicBoolean缓存
原子更新布尔类型,内部使用int类型的value存储1和0表示true和false,底层也是对int类型的原子操做。安全
(2)AtomicInteger架构
原子更新int类型。分布式
(3)AtomicLongide
原子更新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<Integer> 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(0);
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<User> 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());
}
复制代码
关于原子类的问题,笔者整理了大概有如下这些:
(1)Unsafe是什么?
(3)Unsafe为何是不安全的?
(4)Unsafe的实例怎么获取?
(5)Unsafe的CAS操做?
(6)Unsafe的阻塞/唤醒操做?
(7)Unsafe实例化一个类?
(8)实例化类的六种方式?
(9)原子操做是什么?
(10)原子操做与数据库ACID中A的关系?
(11)AtomicInteger怎么实现原子操做的?
(12)AtomicInteger主要解决了什么问题?
(13)AtomicInteger有哪些缺点?
(14)ABA是什么?
(15)ABA的危害?
(16)ABA的解决方法?
(17)AtomicStampedReference是怎么解决ABA的?
(18)实际工做中遇到过ABA问题吗?
(19)CPU的缓存架构是怎样的?
(20)CPU的缓存行是什么?
(21)内存屏障又是什么?
(22)伪共享是什么缘由致使的?
(23)怎么避免伪共享?
(24)消除伪共享在java中的应用?
(25)LongAdder的实现方式?
(26)LongAdder是怎么消除伪共享的?
(27)LongAdder与AtomicLong的性能对比?
(28)LongAdder中的cells数组是无限扩容的吗?
关于原子类的问题差很少就这么多,都能回答上来吗?点击下面的连接能够直接到相应的章节查看:
死磕 java原子类之AtomicStampedReference源码分析
原子类系列源码分析到此就结束了,虽然分析的类比较少,可是牵涉的内容很是多,特别是操做系统底层的知识,好比CPU指令、CPU缓存架构、内存屏障等。
下一章,咱们将进入“同步系列”,同步最多见的就是各类锁了,这里会着重分析java中的各类锁、各类同步器以及分布式锁相关的内容。
欢迎关注个人公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一块儿畅游源码的海洋。