AtomicInteger解析

Java中Unsafe类详解: http://www.cnblogs.com/mickole/articles/3757278.html
通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:
或者变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作时“原子性”的。Doug Lea在未将backport-util-concurrent合并到JSR 166里面来之前,是采用纯Java实现的,于是不可避免的采用了synchronized关键字。
public final synchronized void set(int newValue); 
public final synchronized int getAndSet(int newValue); 
public final synchronized int incrementAndGet(); 

同时在变量上使用了volatile来保证get()的时候不用加锁。
尽管synchronized的代价还是很高的,但是在没有JNI的手段下纯Java语言还是不能实现此操作的。而volatile只能保证变量改变时,对其他线程可以立即看到,因为volatile标注的变量,每次都是从内存中直接读取;同时当多线程修改值是,是不安全的。
今天我们来看一下,JUC包下的AtomicInteger和AtomicIntegerArray
测试主类:
package juc.automic;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
 * 测试AtomicInteger
 * @author donald
 * 2017年2月28日
 * 下午7:01:47
 */
public class TestAtomicInteger {
	public static void main(String[] args) {
		//测试 AtomicInteger
		AtomicInteger testInteger = new AtomicInteger(0);
		System.out.println("=======get:"+testInteger.get());
		System.out.println("=======addAndGet:"+testInteger.addAndGet(3));
		System.out.println("=======incrementAndGet:"+testInteger.incrementAndGet());
		System.out.println("=======decrementAndGet:"+testInteger.decrementAndGet());
		System.out.println("=======getAndAdd:"+testInteger.getAndAdd(2));
		System.out.println("=======getAndIncrement:"+testInteger.getAndIncrement());
		System.out.println("=======getAndDecrement:"+testInteger.getAndDecrement());
		System.out.println("=======getAndSet:"+testInteger.getAndSet(7));
		System.out.println("=======compareAndSet:"+testInteger.compareAndSet(7, 8));
		System.out.println("=======get:"+testInteger.get());
		System.out.println("=======compareAndSet:"+testInteger.weakCompareAndSet(8, 9));
		System.out.println("=======get:"+testInteger.get());
		testInteger.lazySet(6);
		System.out.println("=======get:"+testInteger.get());
		//测试 AtomicIntegerArray
		AtomicIntegerArray testArray = new AtomicIntegerArray(10);
		testArray.set(0, 1);
		System.out.println("Array=======get:"+testArray.get(0));
		System.out.println("Array=======getAndAdd:"+testArray.getAndAdd(0, 2));
		System.out.println("Array=======getAndIncrement:"+testArray.getAndIncrement(0));
		System.out.println("Array=======getAndDecrement:"+testArray.getAndDecrement(0));
		System.out.println("Array=======getAndSet:"+testArray.getAndSet(0, 5));
		System.out.println("Array=======incrementAndGet:"+testArray.incrementAndGet(0));
		System.out.println("Array=======decrementAndGet:"+testArray.decrementAndGet(0));
		System.out.println("Array=======addAndGet:"+testArray.addAndGet(0, 3));
				
	}
}

AtomicInteger和AtomicIntegerArray的方法一看方法名,就知道其作用,我们今天来看一下,方法的具体实现;
//AtomicInteger
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    //JNI ,硬件级别的原子操作
    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); }
    }
   //volatile 值的修改对所欲线程可见
    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
}

相关方法:
 /**
     * Gets the current value.
     *
     * @return the current value
     */
    public final int get() {
        return value;
    }

/**
 * Sets to the given value.
 *
 * @param newValue the new value
 */
public final void set(int newValue) {
    value = newValue;
}


/**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

lazySet含义:最后设置为给定值。延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。如果还是难以理解,这里就类似于启动一个后台线程如执行修改新值的任务,原线程就不等待修改结果立即返回(这种解释其实是不正确的,但是可以这么理解)。

 public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }
       public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

从上面两个方法,可以看出,getAndIncrement和getAndSet,保证原子性操作,通过compareAndSet(CAS原理)


  public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

compareAndSet方法含义:
如果当前值== 预期值,则以原子方式将该值设置为给定的更新值。
如果成功就返回true,否则返回false,并且不修改原值。
 public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }


weakCompareAndSet方法含义:
如果当前值== 预期值,则以原子方式将该设置为给定的更新值。JSR规范中说:以原子方式读取和有条件地写入变量但不创建任何happen-before 排序,因此不提供与除weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。

//unsafe
public final class Unsafe
{

    private static native void registerNatives();

    private Unsafe()
    {
    }

    public static Unsafe getUnsafe()
    {
        Class class1 = Reflection.getCallerClass(2);
        if(class1.getClassLoader() != null)
            throw new SecurityException("Unsafe");
        else
            return theUnsafe;
    }

    public native int getInt(Object obj, long l);

    public native void putInt(Object obj, long l, int i);
    ....
     public final native boolean compareAndSwapObject(Object obj, long l, Object obj1, Object obj2);

    public final native boolean compareAndSwapInt(Object obj, long l, int i, int j);

    public final native boolean compareAndSwapLong(Object obj, long l, long l1, long l2);
}

再来看:
public class AtomicIntegerArray implements java.io.Serializable {
    private static final long serialVersionUID = 2862133569453604235L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final int base = unsafe.arrayBaseOffset(int[].class);
    private static final int shift;
    //存放数组元素
    private final int[] array;

    static {
        int scale = unsafe.arrayIndexScale(int[].class);
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }
}
//构造
 public AtomicIntegerArray(int length) {
        array = new int[length];
    }

//获取值
public final int get(int i) {
        return getRaw(checkedByteOffset(i));
    }

    private int getRaw(long offset) {
        return unsafe.getIntVolatile(array, offset);
    }
//设置
  public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }


  public final int getAndSet(int i, int newValue) {
        long offset = checkedByteOffset(i);
        while (true) {
            int current = getRaw(offset);
            if (compareAndSetRaw(offset, current, newValue))
                return current;
        }
    }
    private boolean compareAndSetRaw(long offset, int expect, int update) {
        return unsafe.compareAndSwapInt(array, offset, expect, update);
    }
     public final boolean compareAndSet(int i, int expect, int update) {
        return compareAndSetRaw(checkedByteOffset(i), expect, update);
    }


从上可以看,相关方法是通过JNI实现

//unsafe
public native int getIntVolatile(Object obj, long l);
public native void putIntVolatile(Object obj, long l, int i);
public native long allocateMemory(long l);
public native long reallocateMemory(long l, long l1);
public native void freeMemory(long l);


猜你喜欢

转载自donald-draper.iteye.com/blog/2359555