Java原子类之终结篇(五)

devtool · · 94 次点击 · 开始浏览    置顶
--- ## 概览 原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。 在java中提供了很多原子类,笔者在此主要把这些原子类分成四大类。 ![atomic](https://gitee.com/alan-tang-tt/yuan/raw/master/死磕%20java并发包/resource/atomic1.png) ## 原子更新基本类型或引用类型 如果是基本类型,则替换其值,如果是引用,则替换其引用地址,这些类主要有: - AtomicBoolean 原子更新布尔类型,内部使用int类型的value存储1和0表示true和false,底层也是对int类型的原子操作。 - AtomicInteger 原子更新int类型。 - AtomicLong 原子更新long类型。 - AtomicReference 原子更新引用类型,通过泛型指定要操作的类。 - AtomicMarkableReference 原子更新引用类型,内部使用Pair承载引用对象及是否被更新过的标记,避免了ABA问题。 - AtomicStampedReference 原子更新引用类型,内部使用Pair承载引用对象及更新的邮戳,避免了ABA问题。 这几个类的操作基本类似,底层都是调用Unsafe的compareAndSwapXxx()来实现,基本用法如下: ```java 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()); } ``` ## 原子更新数组中的元素 原子更新数组中的元素,可以更新数组中指定索引位置的元素,这些类主要有: - AtomicIntegerArray 原子更新int数组中的元素。 - AtomicLongArray 原子更新long数组中的元素。 - AtomicReferenceArray 原子更新Object数组中的元素。 这几个类的操作基本类似,更新元素时都要指定在数组中的索引位置,基本用法如下: ```java 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)); } ``` ## 原子更新对象中的字段 原子更新对象中的字段,可以更新对象中指定字段名称的字段,这些类主要有: - AtomicIntegerFieldUpdater 原子更新对象中的int类型字段。 - AtomicLongFieldUpdater 原子更新对象中的long类型字段。 - AtomicReferenceFieldUpdater 原子更新对象中的引用类型字段。 这几个类的操作基本类似,都需要传入要更新的字段名称,基本用法如下: ```java 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到不同的段上去更新,最后再把这些段的值相加得到最终的值,这些类主要有: - Striped64 下面四个类的父类。 - LongAccumulator long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。 - LongAdder long类型的累加器,LongAccumulator的特例,只能用来计算加法,且从0开始计算。 - DoubleAccumulator double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。 - DoubleAdder double类型的累加器,DoubleAccumulator的特例,只能用来计算加法,且从0开始计算。 这几个类的操作基本类似,其中DoubleAccumulator和DoubleAdder底层其实也是用long来实现的,基本用法如下: ```java 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数组是无限扩容的吗? 关于原子类的问题差不多就这么多,都能回答上来吗? ## 彩蛋 原子类系列源码分析到此就结束了,虽然分析的类比较少,但是牵涉的内容非常多,特别是操作系统底层的知识,比如CPU指令、CPU缓存架构、内存屏障等。 下一章,我们将进入“同步系列”,同步最常见的就是各种锁了,这里会着重分析java中的各种锁、各种同步器以及分布式锁相关的内容。 ---
94 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传