死磕 java原子类之终结篇(面试题)
概览
原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。
在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<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中的各种锁、各种同步器以及分布式锁相关的内容。
欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。

死磕 java原子类之终结篇(面试题)的更多相关文章
- 死磕 java同步系列之终结篇
简介 同步系列到此就结束了,本篇文章对同步系列做一个总结. 脑图 下面是关于同步系列的一份脑图,列举了主要的知识点和问题点,看过本系列文章的同学可以根据脑图自行回顾所学的内容,也可以作为面试前的准备. ...
- 死磕 java线程系列之终篇
(手机横屏看源码更方便) 简介 线程系列我们基本就学完了,这一个系列我们基本都是围绕着线程池在讲,其实关于线程还有很多东西可以讲,后面有机会我们再补充进来.当然,如果你有什么好的想法,也可以公从号右下 ...
- 死磕 java集合之终结篇
概览 我们先来看一看java中所有集合的类关系图. 这里面的类太多了,请放大看,如果放大还看不清,请再放大看,如果还是看不清,请放弃. 我们下面主要分成五个部分来逐个击破. List List中的元素 ...
- 死磕 java同步系列之AQS起篇
问题 (1)AQS是什么? (2)AQS的定位? (3)AQS的实现原理? (4)基于AQS实现自己的锁? 简介 AQS的全称是AbstractQueuedSynchronizer,它的定位是为Jav ...
- 死磕 java同步系列之AQS终篇(面试)
问题 (1)AQS的定位? (2)AQS的重要组成部分? (3)AQS运用的设计模式? (4)AQS的总体流程? 简介 AQS的全称是AbstractQueuedSynchronizer,它的定位是为 ...
- 【死磕Java并发】----- 死磕 Java 并发精品合集
[死磕 Java 并发]系列是 LZ 在 2017 年写的第一个死磕系列,一直没有做一个合集,这篇博客则是将整个系列做一个概览. 先来一个总览图: [高清图,请关注"Java技术驿站&quo ...
- 死磕 java同步系列之volatile解析
问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile可以说是Java ...
- 死磕 java同步系列之redis分布式锁进化史
问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点? (4)redis实现分布式锁有没有现成的轮子可以使用? 简介 Redis(全称:R ...
- 【死磕Java并发】-----J.U.C之AQS:CLH同步队列
此篇博客全部源代码均来自JDK 1.8 在上篇博客[死磕Java并发]-–J.U.C之AQS:AQS简单介绍中提到了AQS内部维护着一个FIFO队列,该队列就是CLH同步队列. CLH同步队列是一个F ...
随机推荐
- 【转】Pro Android学习笔记(九):了解Content Provider(下下)
Content provider作为信息的读出,比较常见的还有文件的读写,最基础的就是二进制文件的的读写,例如img文件,音频文件的读写.在数据库中存放了该文件的路径,我们可以通过ContentPro ...
- 【转】Pro Android学习笔记(三):了解Android资源(上)
在Android开发中,资源包括文件或者值,它们和执行应用捆绑,无需在源代码中写死,因此我们可以改变或替换他们,而无需对应用重新编译. 了解资源构成 参考阅读Android学习笔记(三八):资源res ...
- 四 akka学习 四种多线程的解决方案
http://blog.csdn.net/chenleixing/article/details/44044243 四种多线程的解决方案
- 参数化之利用CSV Data Set Config从文件读取参数并关联变量
众所周知,在进行接口测试的过程中,需要创建不同的场景(不同条件的输入,来验证不同的入参的返回结果).因而,在日常的自动化接口监控或商品监控等线上监控过程中,需要配置大量的入参来监控接口的返回是否正确. ...
- ENFP喜欢的职业
外向(E)+直觉(N)+情感(F)+知觉(P). 1. 设计:设计本身很能满足ENFP对工作的各种要求,但是有个附加条件就是,这份工作不能让ENFP长时间的一个人工作,没机会和别人交流,也就是说有一个 ...
- Silverlight 5 系列学习之二
昨天学习了一下Silverlight基础感觉也没有什么特别之处,不过圈里朋友劝我不要深入学习了,因为ms已不再爱他的这个孩子了,好吧那就把上些简单的东西稍微过一下吧,要不然公司有什么需求要改的小弟不会 ...
- linux jar 后台运行
在linux系统中可以利用nohup来执行任何命令,并把命令自动调到linux后台运行,不锁定当前ssh窗口,也不会被ctrl + c,alt + F4之类打断程序的动行.如: nohup java ...
- Linux 程式減肥(strip & objcopy)(转载)
转自:http://calamaryshop.blogspot.com/2011/11/linux-strip-objcopy.html 對於設計嵌入式Linux系統的研發人員來說,記憶體的空間是非常 ...
- SQL Server(三)
一.数据库操作 create database 数据库名称 ——创建drop database 数据库名称 ——删除use 数据库名称 ——使用go 两条SQL语句之间分隔 二.表的操作 create ...
- npm ERR! mathine_call@1.0.0 dev: `webpack-dev-server --inline --progress --config build/webpack.dev.conf.js` npm ERR! Exit status 1
internal/modules/cjs/loader.js:583 throw err; ^ Error: Cannot find module 'webpack' at Function.Modu ...