Unsafe使用

Posted qulllee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unsafe使用相关的知识,希望对你有一定的参考价值。

Unsafe

Unsafe类是JDK底层类库提供的、基于Java内存模型访问底层的机制,通过该类可以实例化对象、直接操作堆外内存(直接内存)、CAS原子操作等。Java基于AQS实现的同步工具类、并发容器,Netty对堆外内存的操作都基于该类实现。

获取Unsafe实例

Unsafe中提供了静态方法getUnsafe可以直接获取到Unsafe对象,但是该方法只允许JDK类库调用,我们平时开发中调用该方法将抛出SecurityException异常。

当然在开发中,我们可以使用反射获取到该类实例。该类中包含一个自身属性Unsafe theUnsafe,通过反射获取该属性即可获取一个初始化后的Unsafe实例。

Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

常用方法

/**
 * 实例化一个类
 * @param var1 要实例化类的Class对象
 */
public native Object allocateInstance(Class<?> var1) throws InstantiationException;

/**
 * 获取一个类中某个属性的位移
 * @param var1 属性的Field
 */
public native long objectFieldOffset(Field var1);

/**
 * 获取某个实例对象的int属性值
 * @param var1 实例对象
 * @param var2 int属性的offset
 */
public native int getInt(Object var1, long var2);

/**
 * 修改实例对象的int属性值
 * @param var1 实例对象
 * @param var2 int属性的offset
 * @param var4 修改值
 */
public native void putInt(Object var1, long var2, int var4);

// 省略boolean、byte、char、short、float、double、long、Object

/**
 * 申请堆外内存
 * @param var1 申请内存大小,单位字节
 * @return 堆外内存的起始地址
 */
public native long allocateMemory(long var1);

/**
 * 释放堆外内存
 * @param var1 堆外内存的起始地址
 */
public native void freeMemory(long var1);

/**
 * CAS修改实例对象int属性值
 * @param var1 实例对象
 * @param var2 int属性值
 * @param var4 int属性oldVal
 * @param var5 int属性修改值newVal
 */
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

// 省略long、Object

/**
 * 唤醒线程
 * 具体使用可以参考 LockSupport.unpark()
 */
public native void unpark(Object var1);

/**
 * 阻塞线程
 * 具体使用可以参考 LockSupport.park()
 */
public native void park(boolean var1, long var2);

代码实例

public class User {
    private int age = 20;

    public static void main(String[] args) {
        // 构造器创建实例
        User user = new User();
        System.out.println(user);
        // age = 20  表明使用构建器创建实例,会初始化实例属性
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());
        user.unsafeSetAge(1);
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());

        user.setAge(2);
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());

        // unsafe创建实例
        user = User.getInstance();
        System.out.println(user);
        // age = 0  表明使用Unsafe创建实例,并不会初始化实例属性
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());
        user.unsafeSetAge(10);
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());

        // 每次调用都会创建新的实例
        user = User.getInstance();
        System.out.println(user);
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());
        user.unsafeSetAge(100);
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());

        // cas修改
        user.unsafeCas(100, 150);
        System.out.println("unsafeGetAge : " + user.unsafeGetAge() + " userGetAge : " +  user.getAge());

    }

    private static Unsafe UNSAFE;
    private static long AGE_OFFSET;

    static {

        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            UNSAFE = (Unsafe) f.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        try {
            AGE_OFFSET = UNSAFE.objectFieldOffset(User.class.getDeclaredField("age"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    public static User getInstance() {
        try {
            return (User)UNSAFE.allocateInstance(User.class);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException("初始化User实例失败");
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int unsafeGetAge() {
        return UNSAFE.getInt(this, AGE_OFFSET);
    }

    public void unsafeSetAge(int age) {
        UNSAFE.putInt(this, AGE_OFFSET, age);
    }

    public void unsafeCas(int expectAge, int newAge) {
        UNSAFE.compareAndSwapInt(this, AGE_OFFSET, expectAge, newAge);
    }

    @Override
    public String toString() {
        return "User{" +
            "age=" + age + "," +
            "hashcode=" + this.hashCode() +
            '}';
    }
}

以上是关于Unsafe使用的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会收到错误“不安全代码可能仅在使用 /unsafe 编译时出现”?

unsafe,fixed与GCHandle

C# 错误CS0227 不安全代码只会在使用 /unsafe 编译的情况下出现

C# 错误CS0227 不安全代码只会在使用 /unsafe 编译的情况下出现

pikachu File Inclusion, Unsafe File Download & Unsafe File Upload

如何修复 no-unsafe-any 规则?