Loading

JUC学习笔记--Atomic原子类

J.U.C 框架学习顺序 http://blog.csdn.net/chen7253886/article/details/52769111

Atomic 原子操作类包

Atomic包 主要是在多线程环境下,无锁的进行原子操作。核心操作是基于UnSafe类实现的CAS方法

CAS

CAS: compareAndSwap :传入两个值:期望值和新值,判断原有值与期望值相等,则给其赋新值,否则不做任何操作。

CAS硬件支持

现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰。

Atomic源码

以AtomicInteger为例(AtomicBoolean也是把boolen转为int)

public class AtomicInteger extends Number implements java.io.Serializable {
    //序列化相关
    private static final long serialVersionUID = 6214790243416807050L;
    //Unsafe类
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //内存地址偏移量
    private static final long valueOffset;
    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }
    //value值,因为无锁使用volatile保证线程加载到最新的值
    private volatile int value;
    //初始化
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    public AtomicInteger() {
    }
    //获取当前值
    public final int get() {
        return value;
    }
    //赋值
    public final void set(int newValue) {
        value = newValue;
    }
    //延迟赋值,不保证新的赋值能立即被其他线程获取到
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }
    //返回旧值并赋新值
    public final int getAndSet(int newValue) {
        for (;;) {// 等同于while(true)
            int current = get();//获取旧值
            if (compareAndSet(current, newValue))//以CAS方式赋值,直到成功返回
                return current;
        }
    }
    //对比期望值与value,不同返回false。相同将update赋给value 返回true
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //与compareAndSet实现相同(可能为了以后更改)
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //自增 i++
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }
    //自减 i--
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }
    //自定义增量数
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    //++i 返回自增后的值
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
    
    //--i 返回减少后的值
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    //返回加上delta后的值
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Returns the String representation of the current value.
     * @return the String representation of the current value.
     */
    public String toString() {
        return Integer.toString(get());
    }


    public int intValue() {
        return get();
    }

    public long longValue() {
        return (long)get();
    }

    public float floatValue() {
        return (float)get();
    }

    public double doubleValue() {
        return (double)get();
    }

}

CAS的ABA问题

ABA问题:有两个线程,x线程读取值为A ,y线程读取值为A 并赋值为B,B线程再修改会A,此时x线程以A为旧值并赋新值仍然是成功的,
因为线程x不知道变量值经过A->B->A的修改。

AtomicStampedReference 解决ABA问题

先看看如何使用:

        AtomicStampedReference<Integer> asr= new AtomicStampedReference<Integer>(1,0);
        int stamp= asr.getStamp();
        System.out.println(asr.compareAndSet(1,2,stamp,stamp+1));//true
        System.out.println(asr.compareAndSet(2,1,stamp,stamp+1));//false
        System.out.println(asr.compareAndSet(1,1,stamp+1,stamp+2));//false
        System.out.println(asr.getReference());//2

对比AtomicInteger,可见AtomicStampedReference的构造函数多了一个参数、compareAndSet方法多了两个参数,看一下源码:

    //对比AtomicInteger构造函数多了stamp参数
    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }


    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }
    //对比AtomicInteger value由int 改为Pair<V>
    private volatile Pair<V> pair;

    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&        //判断期望值与旧值是否相同
            expectedStamp == current.stamp &&   //判断期望版本戳与旧版本戳是否相同
            ((newReference == current.reference &&
              newStamp == current.stamp) ||    //如果值和戳没变化不执行下一行cas赋值代码
             casPair(current, Pair.of(newReference, newStamp)));//cas赋值代码
    }
    //还是调用unsafe类
    private boolean casPair(Pair<V> cmp, Pair<V> val) {
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }
posted @ 2016-12-03 18:17  JavaNoob  阅读(413)  评论(0编辑  收藏  举报