atomicLong源码分析详解

atomicLong的字段和实例方法

atomicLong源码分析详解_新特性

源码分析

注意:源码分析都放在源码里面

package java.util.concurrent.atomic;
import java.util.function.LongUnaryOperator;
import java.util.function.LongBinaryOperator;
import sun.misc.Unsafe;

/**
一个long值可以用原子更新。 有关原子变量属性的描述,请参阅java.util.concurrent.atomic包规范。 
一个AtomicLong用于诸如原子增量的序列号的应用中,不能用作Long的替代物 。 但是,该类确实扩展了Number ,以允许使用基于数字类的工具和实用程序的统一访问。 
从以下版本开始: 
1.5 
另请参见: 
Serialized Form 
 * @since 1.5
 * @author Doug Lea
 */
public class AtomicLong extends Number implements java.io.Serializable {
/*serialVersionUID 有什么作用?该如何使用?
serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途:
1. 把对象序列化成字节码,保存到指定介质上(如磁盘等)
2. 用于网络传输
详情看下面参考文献1  
这是链接https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/what-is-a-serialversionuid-and-why-should-i-use-it.md
 */    
    private static final long serialVersionUID = 1927816293512124184L;

    // 设置为使用Unsafe.compareAndSwapInt进行更新
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

/*
记录底层JVM是否支持longs的无锁compareAndSwap。 
虽然Unsafe.compareAndSwapLong方法在任何一种情况下都可以工作,
但是应该在Java级别处理一些构造以避免锁定用户可见的锁。
 */    
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

/*
返回底层JVM是否支持longs的无锁CompareAndSet。 仅调用一次并缓存在VM_SUPPORTS_LONG_CAS中。
 */
    private static native boolean VMSupportsCS8();

/*  
下面这段静态代码块是用于获取valueOffset
 */
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

//  这个就是AtomicBoolean的关键值,value
    private volatile long value;

    /**
     * 用给定的初始值创建一个新的AtomicLong
     * @param initialValue initialValue - 初始值 
     */
    public AtomicLong(long initialValue) {
        value = initialValue;
    }

    /**
     * 创建一个新的AtomicLong,初始值为 0 。 
     */
    public AtomicLong() {
    }



    /**
     * 获取当前值。 
     *
     * @return 当前值 
     */
    public final long get() {
        return value;
    }

    /**
     * 设置为给定值。
     *
     * @param newValue newValue - 新价值 
     */
    public final void set(long newValue) {
        value = newValue;
    }

    /**
     * 最终设定为给定值。 
     *
     * @param newValue newValue - 新价值 
     * @since 1.6
     */
    public final void lazySet(long newValue) {
        unsafe.putOrderedLong(this, valueOffset, newValue);
    }
/**
 * 这里抛出一个疑问?到底set()和lazySet()有什么区别?
 * 1.首先set()是对volatile变量的一个写操作, 我们知道volatile的write
 * 为了保证对其他线程的可见性会追加以下两个Fence(内存屏障)如下:
 *   1.StoreStore 在intel cpu 不存在[写写]重排序
 *   2.StoreLoad 这个是所有内存屏障里最耗性能的内存屏障相关参考Doug Lea大大的cookbook (http://g.oswego.edu/dl/jmm/cookbook.html)
 * Doug Lea大大又说了, lazySet()省去了StoreLoad屏障, 只留下StoreStore
 * 总结:set()和volatile具有一样的效果(能够保证内存可见性,能够避免指令重排序),
 * 但是使用lazySet不能保证其他线程能立刻看到修改后的值(有可能发生指令重排序)。
 * 简单点理解:lazySet比set()具有性能优势,但是使用场景很有限。
 * 在网上没有找到lazySet和set的性能数据对比,
 * 而且CPU的速度很快的,应用的瓶颈往往不在CPU,
 * 而是在IO、网络、数据库等。对于并发程序要优先保证正确性,
 * 然后出现性能瓶颈的时候再去解决。因为定位并发导致的问题,往往要比定位性能问题困难很多。
 */


    /**
     * 将原子设置为给定值并返回旧值。
     * @param newValue newValue - 新的价值 
     * @return 以前的值 
     */
    public final long getAndSet(long newValue) {
        return unsafe.getAndSetLong(this, valueOffset, newValue);
    }
/*
    如下,实际unsafe.getAndSetLong(this, valueOffset, newValue),还是使用compareAndSwapLong这个本地方法。什么是本地方法 native method?
简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern “C”告知C++编译器去调用一个C的函数。 “A native method is a Java method whose implementation is provided by non-java code.” 在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的。

    public final long getAndSetLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var4));

        return var6;
    }
CAS 
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V 
值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么 
都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成 
操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程 
不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS 
操作即时没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。


 */    

    /**
     * 如果当前值为 ==为预期值,则将该值原子设置为给定的更新值。 
     *
     * @param expect expect - 预期值
     * @param update update - 新值 
     * @return true如果成功 

     */
    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }
/*
这里是直接调用unsafe.compareAndSwapLong();CAS操作如上面所说
 */    

    /**
     * 如果当前值为== ,则将原值设置为给定的更新值。 May fail spuriously and does not provide ordering guarantees ,所以只是很少适合替代compareAndSet 。 
     *
     * @param expect expect - 预期值 
     * @param update update - 新值 
     * @return true如果成功 
     */
    public final boolean weakCompareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }
    /*
    延续上面的提问,为什么可能会失败?这里给我跳转到jdk 的javadoc,我把原文发出来,里面很详细,这里简单描述一下
    这里是链接:
    一个原子类也支持weakCompareAndSet方法,该方法有适用性的限制。在一些平台上,在正常情况下weak版本比compareAndSet更高效,
    但是不同的是任何给定的weakCompareAndSet方法的调用都可能会返回一个虚假的失败( 无任何明显的原因 )。一个失败的返回意味着,操作将会重新执行如果需要的话,
    重复操作依赖的保证是当变量持有expectedValue的值并且没有其他的线程也尝试设置这个值将最终操作成功。( 一个虚假的失败可能是由于内存冲突的影响,而和预期值(expectedValue)和当前的值是否相等无关 )。
    此外weakCompareAndSet并不会提供排序的保证,即通常需要用于同步控制的排序保证。然而,这个方法可能在修改计数器或者统计,这种修改无关于其他happens-before的程序中非常有用。
    当一个线程看到一个通过weakCompareAndSet修改的原子变量时,它不被要求看到其他变量的修改,即便该变量的修改在weakCompareAndSet操作之前。
    weakCompareAndSet实现了一个变量原子的读操作和有条件的原子写操作,但是它不会创建任何happen-before排序,
    所以该方法不提供对weakCompareAndSet操作的目标变量以外的变量的在之前或在之后的读或写操作的保证。
     */    

    /**
     * 原子上增加一个当前值。
     * @return 以前的值 
     */
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }
/*
public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

        return var6;
    }
public native long getLongVolatile(Object var1, long var2);//使用本地方法,取的相对这个对象var1的偏移地址为var2的值,简单来说就是拿到value字段的值
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);//CAS操作保证原子性

 */


    /**
     * 原子减1当前值。
     *
     * @return 以前的值 
     */
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }
/*
原理和上面一样,数值变成-1
 */

    /**
     * 将给定的值原子地添加到当前值。 
     *
     * @param delta delta - 要添加的值 
     * @return 以前的值 
     */
    public final long getAndAdd(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta);
    }
/*
原理和上面一样,数值变成delta
 */    

    /**
     * 原子上增加一个当前值。
     *
     * @return 更新的值
     */
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }

/*
原理和上面一样,数值变成1;返回时旧值+1就是新值,所以返回是新值
 */    


    /**
     * 原子减1当前值。 
     *
     * @return 更新的值 
     */
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }
/*
原理和上面一样,数值变成-1;返回时旧值-1就是新值,所以返回是新值
 */    

    /**
     * 将给定的值原子地添加到当前值。 
     *
     * @param delta delta - 要添加的值 
     * @return 更新的值 
     */
    public final long addAndGet(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
    }
/*
原理和上面一样,数值变成delta;返回时旧值+delta就是新值,所以返回是新值
 */    

    /**
     * 用应用给定函数的结果原子更新当前值,返回上一个值。 
     * 该功能应该是无副作用的,因为尝试的更新由于线程之间的争用而失败时可能会被重新应用。
     *
     * @param updateFunction updateFunction - 无副作用的功能
     * @return 以前的值 
     * @since 1.8
     */
    public final long getAndUpdate(LongUnaryOperator updateFunction) {
        long prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsLong(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    /**
     * 使用给定函数的结果原子更新当前值,返回更新的值。 该功能应该是无副作用的,因为尝试的更新由于线程之间的争用而失败时可能会被重新应用。
     * @param updateFunction updateFunction - 无副作用的功能 
     * @return 更新的值 
     * @since 1.8
     */
    public final long updateAndGet(LongUnaryOperator updateFunction) {
        long prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsLong(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }

    /**
     * 使用给定函数应用给当前值和给定值的结果原子更新当前值,返回上一个值。
     * 该功能应该是无副作用的,因为尝试的更新由于线程之间的争用而失败时可能会被重新应用。 该函数应用当前值作为其第一个参数,给定的更新作为第二个参数。
     *
     * @param x x - 更新值 
     * @param accumulatorFunction accumulatorFunction - 两个参数的无效副作用 
     * @return 以前的值 
     * @since 1.8
     */
    public final long getAndAccumulate(long x,
                                       LongBinaryOperator accumulatorFunction) {
        long prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsLong(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    /**
     * 使用将给定函数应用于当前值和给定值的结果原子更新当前值,返回更新后的值。 
     * 该功能应该是无副作用的,因为尝试的更新由于线程之间的争用而失败时可能会被重新应用。 该函数应用当前值作为其第一个参数,给定的更新作为第二个参数。
     *
     * @param x x - 更新值 
     * @param accumulatorFunction accumulatorFunction - 两个参数的无效副作用 
     * @return the updated value
     * @since 1.8
     */
    public final long accumulateAndGet(long x,
                                       LongBinaryOperator accumulatorFunction) {
        long prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsLong(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }

/*
以上的getAndUpdate(LongUnaryOperator updateFunction);updateAndGet(LongUnaryOperator updateFunction);getAndAccumulate(long x,LongBinaryOperator accumulatorFunction);accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)
四个方法都是用同样的原理;
prev = get();获取当前的value字段的值
next = updateFunction.applyAsLong(prev);updateFunction - 无副作用的功能;这是java8 的新特性。lz还不会。
总体来讲:这个方法就是去那当前的值,拿到当前值并返回当前值,但是有一点就是保证了返回的时候值还是那个值,没有被改变,试想就get()拿到了value,但是再多线程的转态可能会被改变,一旦改变了,下面的CAS操作就会失败;
只有CAS操作成功了就返回值,说明get()之后,value值没有被其他线程改变,这是乐观锁的思想
 */ 

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

    /**
     * Returns the value of this {@code AtomicLong} as an {@code int}
     * after a narrowing primitive conversion.
     * @jls 5.1.3 Narrowing Primitive Conversions
     */
    public int intValue() {
        return (int)get();
    }

    /**
     * Returns the value of this {@code AtomicLong} as a {@code long}.
     */
    public long longValue() {
        return get();
    }

    /**
     * Returns the value of this {@code AtomicLong} as a {@code float}
     * after a widening primitive conversion.
     * @jls 5.1.2 Widening Primitive Conversions
     */
    public float floatValue() {
        return (float)get();
    }

    /**
     * Returns the value of this {@code AtomicLong} as a {@code double}
     * after a widening primitive conversion.
     * @jls 5.1.2 Widening Primitive Conversions
     */
    public double doubleValue() {
        return (double)get();
    }

}

总结:

  1. lz的Java8新特性掌握不好,看完这个我要去看看Java8的新特性了
  2. 下一步就对unsafe这个直接操作内存的类做一下分析