概览

原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。

原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。

在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(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 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());

}

问题

关于原子类的问题,笔者整理了大概有以下这些:

(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中的各种锁、各种同步器以及分布式锁相关的内容。

欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。

java 原子类能做什么_死磕 java原子类之终结篇(面试题)相关推荐

  1. java任务流程_死磕 java线程系列之线程池深入解析——普通任务执行流程

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 前面我们一起学习了Java中 ...

  2. java 同步锁_死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  3. java ee是什么_死磕 java集合之HashSet源码分析

    问题 (1)集合(Collection)和集合(Set)有什么区别? (2)HashSet怎么保证添加元素不重复? (3)HashSet是否允许null元素? (4)HashSet是有序的吗? (5) ...

  4. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  5. java方法和变量修饰符有哪些_死磕Java基础---类,变量和方法的修饰符

    欢迎关注微信公众号:一个自学的程序员 类修饰符 对于类的修饰符,毫无疑问是用来修饰类的,那么,修饰类的修饰符都有哪些? 有如下这些: 1. abstract 2. final 3. private 4 ...

  6. hashmap修改对应key的值_死磕 java集合之HashMap源码分析

    简介 HashMap采用key/value存储结构,每个key对应唯一的value,查询和修改的速度都很快,能达到O(1)的平均时间复杂度.它是非线程安全的,且不保证元素存储的顺序: 继承体系 Has ...

  7. java线程池深入讲解_死磕 java线程系列之线程池深入解析——生命周期

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 上一章我们一起重温了下线程的 ...

  8. 2019死磕java面试题_死磕 java同步系列之开篇

    简介 同步系列,这是彤哥想了好久的名字,本来是准备写锁相关的内容,但是java中的CountDownLatch.Semaphore.CyclicBarrier这些类又不属于锁,它们和锁又有很多共同点, ...

  9. java unsafe 详解_死磕 java魔法类之Unsafe解析

    问题 (1)Unsafe是什么? (2)Unsafe具有哪些功能? (3)Unsafe为什么是不安全的? (4)怎么使用Unsafe? 简介 本章是java并发包专题的第一章,但是第一篇写的却不是ja ...

最新文章

  1. 用Golang写一个搜索引擎(0x05)--- 文本相关性排序
  2. 【董天一】IPFSFilecoin和复制证明
  3. 栈溢出脚本_漏洞练习之网络编程与堆栈溢出技术
  4. VMware vSphere 文档--总目录vSphere 5.5 6.0 6.5 6.7 7.0
  5. 利用容器来拆分字符串
  6. linux 更换 镜像源
  7. html+css入门(参考b站黑马
  8. D-link 带USB口无线路由器 配置网络共享打印机
  9. 区块链大戏上演!陈伟星VS朱啸虎公开互怼数个回合 | 区块链日报
  10. Web安全工程师成长路线
  11. Hanoi Tower Troubles Again! ZOJ - 1239
  12. weui.js slider的使用笔记
  13. 安装esxi6.X系统过程
  14. 基于java的心理健康网站的设计与实现_基于JavaEE心理健康教育网站的设计与开发.doc...
  15. C++病毒——感染C/C++源文件
  16. 上善若水——甲骨文——文字文化
  17. 一. button按钮防止重复点击(5秒内设置点击一次)
  18. msp430的DCO校准值被清除后该如何处理
  19. 可穿戴电子设备老化测试指南
  20. 计算机翻译turtle,turtle是什么意思_turtle翻译_读音_用法_翻译

热门文章

  1. gossip 区块链_区块链中的P2P
  2. java实现复制粘贴的计算器_软帝学院教你用java编写计算器(三)
  3. linux查看tcp络连接日志,Linux监控TCP连接数并触发日志记录
  4. 如何用极路由新插件【搜狐视频】进行远程下载
  5. 物联网协议比较 MQTT CoAP RESTful/HTTP XMPP
  6. emd实现信息隐藏_EMD算法原理与实现
  7. 备份恢复linux,备份和恢复Linux系统
  8. java+的数组分割符_Java:使用分隔符连接基元数组
  9. python使用xlrd读取xlsx文件_$ 用python处理Excel文档(1)——用xlrd模块读取xls/xlsx文档...
  10. 男孩读计算机好还是铁路学校好,中专学计算机好还是铁路好?