1.介绍一下 Atomic 原子类
Atomic
翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
所以,所谓原子类说简单点就是具有原子 / 原子操作特征的类。
并发包 java.util.concurrent
的原子类都存放在 java.util.concurrent.atomic
下,如下图所示:
2. JUC 包中的原子类是哪4类?
2.1. 基本类型
使用原子的方式更新基本类型:
- AtomicInteger : 整型原子类
- AtomicLong: 长整型原子类
- AtomicBoolean: 布尔型原子类
2.2.数组类型
使用原子的方式更新数组里的某个元素:
- AtomicIntegerArray: 整型数组原子类
- AtomicLongArray: 长整型数组原子类
- AtomicReferenceArray: 引用类型数组原子类
2.3. 引用类型
使用原子的方式更新引用类型:
- AtomicReference: 引用类型原子类
- AtomicStampedReference: 原子更新带有版本号的引用类型。该类将整型数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
- AtomicMarkableReference: 原子更新带有标记位的引用类型。
2.4.对象属性修改类型
- AtomicIntegerFieldUpdater: 原子更新整型字段的更新器
- AtomicLongFieldUpdater: 原子更新长整型字段的更新器
- AtomicMarkableReference: 原子更新带有标记位的引用类型
3. 讲一下 AtomicInteger 的使用
AtomicInteger 类常用方法:
// 获取当前的值
public final int get()
// 获取当前的值,并设置新的值
public final int getAndSet(int newValue)
// 获取当前的值,并自增
public final int getAndIncrement()
// 获取当前的值,并自减
public final int getAndDecrement()
// 获取当前的值,并加上delta
public final int getAndAdd(int delta)
// 如果输入的数值等于预期值(expect),则以原子方式将该值设置为输入值(update)
boolean compareAndSet(int expect , int update)
// 最终设置为newValue,使用lazySet设置之后可能导致其他线程在之后的一小段时间还是可以读到旧的值
public final void lazySet(int newValue)
AtomicInteger 类的使用示例:
使用 AtomicInteger
之后,不用对 increment()
方法加锁也可以保证线程安全:
class AtomicIntegerTest {
private AtomicInteger count = new AtomicInteger();
//使用AtomicInteger之后,不需要对该方法加锁,也可以实现线程安全。
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
4.简单介绍一下 AtomicInteger 类的原理
AtomicInteger
线程安全原理简单分析,AtomicInteger
类的部分源码:
// 更新操作时提供“比较并替换”的作用
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try{
valueOffset = unsafe.objectFieldOffset(AutomicInteger.class.getDeclaredField("value"));
}catch(Exception ex){
throw new Error(ex);
}
}
private volatile int value;
AtomicInteger
类主要利用 CAS(compare and swap)
+ volatile
和 native
方法来保证原子操作,从而避免 synchronized
的高开销,执行效率大为提升。
CAS 的原理是: 拿期望的值和原本的一个值作比较,如果相同则更新成新的值。Unsafe
类的 ObjectFieldOffset()
方法是一个本地方法,这个方法用来拿到 “原来的值” 的内存地址,返回值是 valueOffset
。另外,value
是一个 volatile
变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。
5. 总结
本篇文章讲解了Java多线程进阶面试题-Atomic原子类。代码和笔记由于纯手打,难免会有纰漏,如果发现错误的地方,请第一时间告诉我,这将是我进步的一个很重要的环节。以后会定期更新算法题目以及各种开发知识点,如果您觉得写得不错,不妨点个关注,谢谢。