Fork me on GitHub

【Java多线程】JUC锁 02. UnSafe

Unsafe

java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作

1. Unsafe调用

Unsafe类是一个单例,调用的方法为getUnsafe

getUnsafe()内部会检查该CallerClass是不是由系统类加载器BootstrapClassLoader加载。

由系统类加载器加载的类调用getClassLoader()会返回null,所以要检查类是否为bootstrap加载器加载只需要检查该方法是不是返回null。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class Unsafe {
private static final Unsafe theUnsafe;

private Unsafe() {
}

@CallerSensitive
public static Unsafe getUnsafe() { //Unsafe类是个单例
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
}

调用Unsafe方法:

  1. 通过JVM参数-Xbootclasspath指定要使用的类为启动类;
  2. 在Unsafe类中有一个成员变量theUnsafe,因此我们可以通过反射将private单例实例的accessible设置为true,然后通过Fieldget方法获取
1
2
3
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

可以通过allocateInstance方法在未调用构造方法的情况下生成对象

1
public native Object allocateInstance(Class<?> var1) throws InstantiationException;

2. 内存操作

1
2
3
public native long allocateMemory(long l);	//分配内存
public native long reallocateMemory(long l, long l1); //扩充内存
public native void freeMemory(long l); //释放内存

3. 对象某字段内存值操作

1
2
3
4
5
6
7
8
9
10
11
12
public native long staticFieldOffset(Field var1);	//对给定Field的定位,返回Field的内存地址偏移量
public native long getLong(long var1); //获取指定offset偏移地址对应的long型Field的值
public native int getIntVolatile(Object var1, long var2); //获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。

public native int arrayBaseOffset(Class<?> var1);
public native int arrayIndexScale(Class<?> var1);

static
{
ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class);
ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class);
}

*_BASE_OFFSET常量: 通过arrayBaseOffset本地方法实现,返回数组中第一个元素的偏移地址

*_INDEX_SCALE常量: 通过arrayIndexScale 本地方法实现,获取数组的转换因子,也就是数组中元素的增量地址

通过二者结合,可定位数组中每个元素在内存中的位置

4. 线程挂起和恢复

1
2
public native void unpark(Object var1);		//终止一个挂起的线程,使其恢复正常
public native void park(boolean var1, long var2); //线程挂起

封装在LockSupport 类中

5. CAS操作

1
2
3
4
5
6
7
8
9
10
/**
* 比较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);

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。

6. 参考资料

JAVA并发编程学习笔记之Unsafe类