Atomic浅谈
引言
Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁(Lock-free)的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。
Atomic包介绍
在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。
Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看下Unsafe的源码,发现Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。
AtomicIntegerArray类需要注意的是,数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部的数组元素进行修改时,不会影响到传入的数组。
AtomicReference是原子更新引用类型,引用类内部成员改变在CAS时不算改变
原理介绍
看源码AtomicXXX类, 实际上都需要native方法的支持, native方法则采用CAS算法, CAS算法是一种无锁算法,CAS是CPU支持的指令.
我们以AtomicInteger源码为例说明:
AtomicInteger在执行++操作的时候首先获取当前的value(volatile类型,获取到的永远是最新的),执行+1操作,然后通过unsafe的CAS,通过将刚才获取的value和当前内存中value进行比较来确定是否将变量更新,如果前后一样说明没有别的线程改动过可以安全更新(ABA问题除外),如果前后不一样则循环检测(线程自旋,没有阻塞),直到变量更新成功
举例:我们可以通过AtomicInteger实现一个Lock-free的计数器
public static void main(String[] args) throws InterruptedException { final CountDownLatch cdl = new CountDownLatch(100); final AtomicInteger ai = new AtomicInteger(0); for(int i=0;i<100;i++){ new Thread(new Runnable() { public void run() { ai.incrementAndGet(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(ai); }
返回:100
多线程下操作是安全的