通常情况下,在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);