Java中Unsafe类详解:[url]http://www.cnblogs.com/mickole/articles/3757278.html[/url]
通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:
或者变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作时“原子性”的。Doug Lea在未将backport-util-concurrent合并到JSR 166里面来之前,是采用纯Java实现的,于是不可避免的采用了synchronized关键字。
同时在变量上使用了volatile来保证get()的时候不用加锁。
尽管synchronized的代价还是很高的,但是在没有JNI的手段下纯Java语言还是不能实现此操作的。而volatile只能保证变量改变时,对其他线程可以立即看到,因为volatile标注的变量,每次都是从内存中直接读取;同时当多线程修改值是,是不安全的。
今天我们来看一下,JUC包下的AtomicInteger和AtomicIntegerArray
测试主类:
AtomicInteger和AtomicIntegerArray的方法一看方法名,就知道其作用,我们今天来看一下,方法的具体实现;
//AtomicInteger
相关方法:
lazySet含义:最后设置为给定值。延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。如果还是难以理解,这里就类似于启动一个后台线程如执行修改新值的任务,原线程就不等待修改结果立即返回(这种解释其实是不正确的,但是可以这么理解)。
从上面两个方法,可以看出,getAndIncrement和getAndSet,保证原子性操作,通过compareAndSet(CAS原理)
compareAndSet方法含义:
如果当前值== 预期值,则以原子方式将该值设置为给定的更新值。
如果成功就返回true,否则返回false,并且不修改原值。
weakCompareAndSet方法含义:
如果当前值== 预期值,则以原子方式将该设置为给定的更新值。JSR规范中说:以原子方式读取和有条件地写入变量但不创建任何happen-before 排序,因此不提供与除weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。
//unsafe
再来看:
从上可以看,相关方法是通过JNI实现
//unsafe
通常情况下,在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);