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
多线程下操作是安全的

 

posted @ 2017-07-11 17:51  暗夜心慌方  阅读(179)  评论(0编辑  收藏  举报