原子操作
原子性这个概念,在多线程编程里是一个老生常谈的问题。所谓的原子性表示一个或者多个操作,要么全部执行完,要么一个也不执行。不能出现成功一部分失败一部分的情况。
在多线程中,如果多个线程同时更新一个共享变量,可能会得到一个意料之外的值。比如 i=1。A 线程更新 i+1 、B线程也更新i+1。通过两个线程并行操作之后可能i的值不等于3。而可能等于2。因为A和B在更新变量i的时候拿到的i可能都是1 这就是一个典型的原子性问题,至于为什么会出现这种情况,CPU的高速缓存,前面文章中我们讲过,多线程里面,要实现原子性,有几种方法,其中一种就是加 volatile 关键字。
而从JDK1.5开始,在J.U.C包中提供了Atomic包,提供了对于常用数据结构的原子操作。它提供了简单、高效、以及线程安全的更新一个变量的方式。
J.U.C 中的原子操作类
由于变量类型的关系,在 J.U.C 中提供了 12 个原子操作的 类。这12个类可以分为四大类
- 原子更新基本类型 AtomicBoolean、AtomicInteger、AtomicLong
- 原子更新数组 AtomicIntegerArray、 AtomicLongArray、 AtomicReferenceArray
- 原子更新引用 AtomicReference、 AtomicReferenceFieldUpdater、AtomicMarkableReference(更新带有标记位的引用类型)
- 原子更新字段 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference
AtomicInteger为例
- 演示代码
- 使用AtomicInteger
- 简单分析一下该类的方法
//get方法只需要直接返回value的值就行,这里的value是通过Volatile修饰的,用来保证可见性
public final int get() {
return value;
}
//通过 do/while 循环,基于 CAS 乐观锁来做原子递增。
//valueOffset 的作用就是从主内存中获得当前 value的值和预期值做一个比较,如果相等,对value做递增并结束循环
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
- 这些JUC提供的类主要就是通过使用了大量的 CAS 操作,保证了多线程访问的安全性,并且对 value 值使用 volatile 关键字修饰,也解决了 CPU 的时间片轮转产生的问题。