Unsafe源码解析

Unsafe

介绍:

​ Java无法直接访问底层操作系统,而是通过本地方法(native)来访问。Java中的Unsafe类为我们提供了管理内存的能力。

创建Unsafe对象

Unsafe类是final的,不允许继承,而且构造函数是私有的,无法通过new的方式创建对象:

public final class Unsafe {
    // 构造函数私有
    private Unsafe() {}
    ...
}

但是我们可以通过反射的方式获取Unsafe对象:

static void testUnsafe() throws NoSuchFieldException, IllegalAccessException {
    Class<?> unsafeClass = Unsafe.class;
    Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(unsafeClass);
    System.out.println(unsafe);
}

Unsafe的主要功能

1,普通读写

可以通过Unsafe去读写一个类的属性,不管这个类是否私有(直接从内存读写)。

// var1: 要读的对象   var2: 对象的偏移地址
public native int getInt(Object var1, long var2);
// var1: 要读的对象   var2: 对象的偏移地址   var3: 新写入的数据
public native void putInt(Object var1, long var2, int var4);

getInt() 方法用于从对象的指定偏移地址处读取一个int类型的数据,需要指定要读取的对象和偏移量。

putInt() 方法用于在对象的指定地址出写入一个int类型的数据,需要执行要读取的对象、偏移量和新写入的值。其他类型的数据也有相同的方法,如Object和Boolean:

// 对象
public native Object getObject(Object var1, long var2);
public native void putObject(Object var1, long var2, Object var4);
// 布尔
public native boolean getBoolean(Object var1, long var2);
public native void putBoolean(Object var1, long var2, boolean var4);

2,Volatile读写

1步骤中的读写是普通的读写操作,无法保证可见性和有序性。而源码中相应的volatile方法就可保证有序性和可见性:

// volatile读
public native int getIntVolatile(Object var1, long var2);
// volatile写
public native void putIntVolatile(Object var1, long var2, int var4);

getIntVolatile方法的作用是在对象的指定位置volatile读一个int类型数据,putIntVolatile是在指定位置volatile写一个数据(volatile解析详见 02-关键字volatile解析)。

3,有序写入

有序写入只保证写入的有序性,并不保证可见性,也就是说,该线程的写入不保证其他线程能立马看到。Unsafe类中,只提供了三种类型的有序写方法:

public native void putOrderedObject(Object var1, long var2, Object var4);

public native void putOrderedInt(Object var1, long var2, int var4);

public native void putOrderedLong(Object var1, long var2, long var4);

4,CAS操作

CAS: Compare And Swap。Unsafe中提供了三种类型Object、int、long的CAS操作。

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

java.util.concurrent中的一些类用到了大量的CAS操作,如AtomicInteger等原子类的写操作和ConcurrentHashMap等类的乐观锁。

5,获取偏移量

我们可以通过偏移量和对象得到对象中属性在内存中所处的位置。Unsafe提供了如下方式:

// 获取静态属性Field在对象中的偏移量
public native long staticFieldOffset(Field var1);
// 获取非静态属性在对象中的偏移量
public native long objectFieldOffset(Field var1);
// 返回Field所在对象
public native Object staticFieldBase(Field var1);
// 返回数组中第一个元素的偏移量
public native int arrayBaseOffset(Class<?> var1);
// 返回数组中第一个元素所占用的内存空间
public native int arrayIndexScale(Class<?> var1);

6,线程调度

public native void unpark(Object var1);

public native void park(boolean var1, long var2);

Unsafe中的park()和unpark()方法实现了对线程的挂起和唤醒。LockSupport中的park()和unpark()方法就是依靠Unsafe中的这两个方法实现的:

// 线程挂起
public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);// 挂起
    setBlocker(t, null);
}
// 线程唤醒
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

7,直接内存操作

在学习JVM时,我们知道Java不可以直接堆内存进行操作,对象内存的分配、回收都是Java虚拟机实现的。Unsafe中提供了直接操作内存的能力。

// 分配内存
public native long allocateMemory(long var1);
// 重新分配内存
public native long reallocateMemory(long var1, long var3);
// 设置内存
public native void setMemory(Object var1, long var2, long var4, byte var6);
// 设置内存
public void setMemory(long var1, long var3, byte var5) {
    this.setMemory((Object)null, var1, var3, var5);
}
// 复制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 复制
public void copyMemory(long var1, long var3, long var5) {
    this.copyMemory((Object)null, var1, (Object)null, var3, var5);
}
// 清除内存
public native void freeMemory(long var1);

8,类加载

// 定义一个类,用于动态创建类
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);
// 用于动态创建一个匿名内部类
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);
// 用于创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类
public native Object allocateInstance(Class<?> var1) throws InstantiationException;
// 判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> var1);
// 保证一个类已经被初始化
public native void ensureClassInitialized(Class<?> var1);

9,内存屏障

// 保证这个屏障之前所有读操作已经完成
public native void loadFence();
// 保证这个屏障之前所有写操作已经完成
public native void storeFence();
// 保证这个屏障之前所有读写操作都完成
public native void fullFence();

参考博客:https://www.jianshu.com/p/db8dce09232d

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值