Unsafe提供了一些可以直接操控内存和线程的底层操作。被JDK广泛应用于java.nio和并发包等实现中,但是不建议在生产环境中使用。
如何使用:
Unsafe类是如此地不安全,以至于JDK开发者增加了很多特殊限制来访问它。
1、私有的构造器
2、工厂方法getUnsafe()的调用器只能被Bootloader加载,否则抛出SecurityException 异常
但是我们可以通过反射获取Unsafe的实例
public static Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe)f.get(null);
} catch (Exception e) {
/* ... */
}
}
看Future.java源码是如何使用Unsafe的
private volatile int state;
private volatile Thread runner;
private volatile WaitNode waiters;
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long stateOffset;
private static final long runnerOffset;
private static final long waitersOffset;
static {
try {
// 获取UNSAFE 的实例
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class> k = FutureTask.class;
// 获取state字段的偏移量
stateOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("state"));
runnerOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));
waitersOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("waiters"));
} catch (Exception e) {
throw new Error(e);
}
}
一些有用的api:
1、objectFieldOffset:获取指定的对象实例变量在对象内存中的偏移量
long stateOffset = UNSAFE.objectFieldOffset
(FutureTask.class.getDeclaredField("state"));
2、compareAndSwapInt:通过比较并替换的机制,修改指定偏移量内存的值
判断内存值stateOffset和期望值expectValue是否一致,一致就修改为新值newValue并返回true,不一致就返回false
UNSAFE.compareAndSwapInt(this, stateOffset, expectValue, newValue)
3、park:使当前线程睡眠
// LockSupport工具类
LockSupport.park(this);
LockSupport.parkNanos(this, nanos);
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
4、unpark:唤醒某个睡眠线程
LockSupport.unpark(thread);
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
CAS:
CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性。
CAS的思想很简单:三个参数,一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false。
缺点:
CAS存在一个很明显的问题,即ABA问题。
如果变量V初次读取的时候是A,并且在准备赋值的时候检查到它仍然是A,那能说明它的值没有被其他线程修改过了吗?
如果在这段期间曾经被改成B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。针对这种情况,java并发包中提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。