AtomicXXX具体怎么实现的呢? 
volatile的基本数据类型+CAS操作。 
volatile保证可见性,当一个线程修改volatile变量时,其他线程拿到的都是修改后的最新值。里面的方法都是对Unsafe方法的封装,而Unsafe里面的方法都是JNI方法,通过调用底层c++方法从而实现指令级的原子操作。 
AtomicInteger count=new AtomicInteger(0);
以下是常见常用的方法:
    //set方法
    public final void set(int newValue) {  
        value = newValue;  
    } 

   //lazyset,没有storeload屏障的set
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    //获取并且设置
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    //原子更新值
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    //weak的CAS,也就是没有volatile语义的CAS,没有加入内存屏障
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    //自增加,返回原先值
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    //自减少,返回原先值
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    //原子性增加delta值
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //自增1,返回最终值
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    //阻塞式更新,并且对prev进行一个IntUnaryOperator操作运算
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }
    //阻塞式更新,并对prev和x,进行二元运算操作。
    public final int getAndAccumulate(int x,IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }

 

lazySet

从上面代码可以看到,由于value是volatile类型,所以普通方法set,就是写入volatile类型变量。此时JVM会插入特定的内存屏障,内存语义具有可见性。

而lazySet呢?看意思是“懒惰”的set,什么意思呢? 
set是内存语义是立即对其他线程可见,则lazySet则是不一定立即可见。 

1.首先set()是对volatile变量的一个写操作, 我们知道volatile的write为了保证对其他线程的可见性会追加以下两个Fence(内存屏障) 

  1)StoreStore // 在intel cpu中, 不存在[写写]重排序, 这个可以直接 省略了 

  2)StoreLoad // 这个是所有内存屏障里最耗性能的

2.lazySet()省去了StoreLoad屏障, 只留下StoreStore 。

“可能是Mustang最后一次JSR166的后续跟进,
我们在Atomic类中添加了一个“lazySet”方法
(AtomicInteger,AtomicReference等)。这是一个利基
在使用微调代码时有时很有用的方法
非阻塞数据结构。语义是
保证写入不会被任何重新排序
上一次写入,但可以与后续操作重新排序
(或等效地,可能不会被其他线程看到)直到
发生一些其他易失性写入或同步动作)。

主要用例是将节点的字段归零
非阻塞数据结构仅仅是为了避免
长期垃圾焚烧; 它适用于无害的情况
如果其他线程暂时看到非空值,但是你可以
喜欢确保结构最终是GCable。
例如,你可以通过避免获得更好的性能
null volatile-write的成本。有几个
沿着这些行的其他用例非基于参考
原子也是如此,因此所有方法都支持该方法
AtomicX课程。

对于那些喜欢用这些操作来思考这些操作的人
常见多处理器的机器级障碍,lazySet
提供了一个先前的商店障碍(也是
在当前平台上没有操作或非常便宜),但没有
商店装载障碍(通常是昂贵的部分
一个易变的写)。“

所以这样一来,在效率上毫无疑问lazySet要比set高很多,可以这样理解,lazySet在intel cpu中,其实就可以看做普通变量的写操作了。

lazySet比set()具有性能优势,但是使用场景很有限。

 

weakCompareAndSet

基于Java8分析,在上述代码中,可以很明显的看到weakCompareAndSet方法和compareAndSet方法,具有相同的实现,但是为啥名字不同呢? 
其实按照Doug Lea本来的设计意图,是想吧weakCompareAndSet设定为一种在性能上更加高效的方法。 
由于compareAndSet和其他读取并更新的操作,拥有相同的内存语义,即具有原子性。 
所以设计了weakCompareAndSet方法,在读上具有通用的原子性,但是写方面不具有volatile语义了,换而言之,weakCompareAndSet的写操作,不能即时被其他线程可见。 

updateAndGet和getAndAccumulate ,这两个方法是Java8新加入的,增加了函数式编程的运用。 
IntUnaryOperator:

@FunctionalInterface
public interface IntUnaryOperator {

    /**
     * 一个操作数的函数
     */
    int applyAsInt(int operand);

    //compose
    default IntUnaryOperator compose(IntUnaryOperator before) {
        Objects.requireNonNull(before);
        return (int v) -> applyAsInt(before.applyAsInt(v));
    }

    //andThen
    default IntUnaryOperator andThen(IntUnaryOperator after) {
        Objects.requireNonNull(after);
        return (int t) -> after.applyAsInt(applyAsInt(t));
    }

    //返回当前运算值
    static IntUnaryOperator identity() {
        return t -> t;
    }
}

IntBinaryOperator: 
IntBinaryOperator则更加简单,也是函数式的方法接口,就只有一个待实现方法:

@FunctionalInterface
public interface IntBinaryOperator {

    /**
     * @param left the first operand
     * @param right the second operand
     * @return the operator result
     */
    int applyAsInt(int left, int right);
}

 

AtomicLong注意之处

有些机器是32位的,所以会把64位long类型volatile拆分成2个32位进行计算,但有些不是的。所以在实现AtomicLong的时候,如果是32位,那就需要加上锁来实现CAS操作。JVM特别的还加了一个判断,来区分是32位:

/**
     * Records whether the underlying JVM supports lockless
     * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
     * method works in either case, some constructions should be
     * handled at Java level to avoid locking user-visible locks.
     */
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

    /**
     * Returns whether underlying JVM supports lockless CompareAndSet
     * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
     */
    private static native boolean VMSupportsCS8();

 

AtomicBoolean注意之处

在AtomicBoolean中,作为volatile变量的,并不是boolean类型,而是一个int类型的0和1来分别表示false和true。

 private volatile int value;

    /**
     * Creates a new {@code AtomicBoolean} with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicBoolean(boolean initialValue) {
        value = initialValue ? 1 : 0;
    }
posted on 2018-09-11 09:49  晒太阳的喵  阅读(262)  评论(1编辑  收藏  举报