最近看java.util.concurrent.atomic包,发现其中大量的用到Unsafe这个类。就在网上查了一下这个类到底是来干嘛的.java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:
1、内存操作
类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应.
/** 分配内存 */
public native long allocateMemory(long l);
/** 扩充内存 */
public native long reallocateMemory(long l, long l1);
/** 释放内存 */
public native void freeMemory(long l);
2、字段的定位与修改
可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
字段的定位:
JAVA中对象的字段的定位可能通过staticFieldOffset方法实现,该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的。
getIntVolatile方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。
getLong方法获取对象中offset偏移地址对应的long型field的值。
数组元素定位:
1. Unsafe类中有很多以BASE_OFFSET结尾的常量,比如ARRAY_INT_BASE_OFFSET,ARRAY_BYTE_BASE_OFFSET等,这些常量值是通过arrayBaseOffset()方法得到的.arrayBaseOffset()方法是一个本地方法,可以获取数组第一个元素的偏移地址.
2. Unsafe类中还有很多以INDEX_SCALE结尾的常量,比如 ARRAY_INT__SCALE,
ARRAY_BYTE_INDEX_SCALE等,这些常量值是通过方法得到的.arrayIndexScale方法也是一个本地方法,可以获取数组的转换因子,也就是数组中元素的增量地址.
结论:
将arrayBaseOffset与arrayIndexScale配合使用,可以定位数组中每个元素在内存中的位置。
public final class Unsafe {
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_INT_INDEX_SCALE;
public native long staticFieldOffset(Field field);
public native int getIntVolatile(Object obj, long l);
public native long getLong(Object obj, long l);
public native int arrayBaseOffset(Class class1);
public native int arrayIndexScale(Class class1);
static
{
ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset([I);
ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale([I);
}
}
下面这个例子演示了简单的修改一个byte[]的数据。
这个例子在eclipse里不能直接编译,要到项目的属性,Java Compiler,Errors/Warnings中Forbidden reference(access rules)中设置为warning。
另外,因为sun.misc.Unsafe包不能直接使用,所有代码里用反射的技巧得到了一个Unsafe的实例。
public class Test {
private static int byteArrayBaseOffset;
public static void main(String[] args) throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe UNSAFE = (Unsafe) theUnsafe.get(null);
System.out.println(UNSAFE);
byte[] data = new byte[10];
System.out.println(Arrays.toString(data));
byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class);
System.out.println(byteArrayBaseOffset);
UNSAFE.putByte(data, byteArrayBaseOffset, (byte) 1);
UNSAFE.putByte(data, byteArrayBaseOffset + 5, (byte) 5);
System.out.println(Arrays.toString(data));
}
}
运行结果:
sun.misc.Unsafe@6af62373
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
24
[1, 0, 0, 0, 0, 5, 0, 0, 0, 0]
3、挂起与恢复
将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。
public class LockSupport {
public static void unpark(Thread thread) {
if (thread != null)
unsafe.unpark(thread);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, 0L);
setBlocker(t, null);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, nanos);
setBlocker(t, null);
}
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(true, deadline);
setBlocker(t, null);
}
public static void park() {
unsafe.park(false, 0L);
}
public static void parkNanos(long nanos) {
if (nanos > 0)
unsafe.park(false, nanos);
}
public static void parkUntil(long deadline) {
unsafe.park(true, deadline);
}
}
4、CAS操作(乐观锁)
首先介绍一下什么是Compare And Swap(CAS)?简单的说就是比较并交换。
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。” Java并发包(java.util.concurrent)中大量使用了CAS操作,涉及到并发的地方都调用了sun.misc.Unsafe类方法进行CAS操作。
在Unsafe中是通过compareAndSwapXXX方法实现的。
/**
* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
*
* @param obj 需要更新的对象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
* @return 如果field的值被更改返回true
*/
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
转载地址:
源码剖析之sun.misc.Unsafe
Java中Unsafe类详解
JAVA并发编程学习笔记之Unsafe类