Unsafe类

JDK 的 rt.jar 包中的 Unsafe 类提供了硬件级别的原子性操作,Unsafe 类中的方法都是 native 方法,他们使用 JNI 的方式访问本地 C++ 实现库。

  • long objectFieldOffset(Field var1):返回指定的变量在所属类中的内存偏移地址,该偏移地址仅仅在该 Unsafe 函数中访问字段时使用。

  • int arrayBaseOffset(Class<?> var1):获取数组中第一个元素的地址。

  • int arrayIndexScale(Class<?> var1):获取数组中一个元素占用的字节。

  • boolean compareAndSwapLong(Object obj, long valueOffset, long except, long update)方法:比较对象 obj 中内存偏移量为 valueOffset 的变量值是否与except 相等。

  • long getLongVolatile(Object var1, long var2):获取对象中偏移量为 var2 的变量对应 volatile 语义的值。

  • void putLongVolatile(Object var1, long var2, long var4):设置 var1 对象中 var2 偏移的类型为 long 的 field 的值为 var4,支持 volatile 语义。

  • putOrderedLong(Object var1, long var2, long var4):设置 var1 对象中 var2 偏移的类型为 long 的 field 的值为 var4。这是一个由延迟的 void putLongVolatile 方法,并且不保证值修改对其他线程立刻可见。只有在变量使用 volatile 修饰并且预计会被意外修改时才是使用该方法。

  • void park(boolean var1, long var2):阻塞当前线程,其中 var1 的值为 false 且 var2 = 0 表示一直阻塞。var2>0 表示等待指定 var2 后阻塞线程会被唤醒。如果 var1=true,并且 var2>0,则表示阻塞的线程到指定时间点会被唤醒,这里 var2 是个绝对时间,是将某个时间店换算为 ms 的值。另外,当其他线程调用了当前线程的 interrupt 方法而中断当前线程时,当前线程也会返回,而当其他线程调用了unPark 方法并且把当前线程作为参数时当前线程也会返回。

  • void unpark(Object var1):唤醒调用 park后阻塞的线程。

  • long getAndSetLong(Object var1, long var2, long var4):获取对象 var1 中偏移量为 var2 的变量volatile 语义的当前值,并设置变量 volatile 语义的值为 var4

    public final long getAndSetLong(Object var1, long var2, long var4) {
         long var6;
         do {
             var6 = this.getLongVolatile(var1, var2);
         } while(!this.compareAndSwapLong(var1, var2, var6, var4));
         return var6;
     }
    
  • long getAndAddLong(Object var1, long var2, long var4):获取对象 var1 中偏移量为 var2 的变量volatile 语义的当前值,并设置变量值为原始值 + var4

    public final long getAndAddLong(Object var1, long var2, long var4) {
         long var6;
         do {
             var6 = this.getLongVolatile(var1, var2);
         } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
     
         return var6;
     }
    

使用 Unsafe 类

public class TestUnsafe {

    /**
     * 获取 UnSafe 对象
     */
    static final Unsafe unsafe = Unsafe.getUnsafe();

    /**
     * 记录变量 state 在类 TestUnsafe 中的偏移量
     */
    static final long stateOffset;

    /**
     * 变量
     */
    private volatile long state = 0;

    static {
        try {
            //获取 state 变量在类 TestUnsafe 中的偏移量
            stateOffset = unsafe.objectFieldOffset(TestUnsafe.class.getDeclaredField("state"));
        } catch (NoSuchFieldException e) {
            System.out.println(e.getLocalizedMessage());
            throw new Error(e);
        }
    }


    public static void main(String[] args) {
        //创建对象,并设置 state 的值为 1
        TestUnsafe test = new TestUnsafe();
        Boolean success = unsafe.compareAndSwapInt(test, stateOffset, 0, 1);
        System.out.println(success);
    }

}
运行结果
java.lang.ExceptionInInitializerError
Caused by: java.lang.SecurityException: Unsafe
	at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
	at com.xiaoming.other.TestUnsafe.<clinit>(TestUnsafe.java:10)
Exception in thread "main" 
查看getUnsafe方法
public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}
//判断 var0 是不是 BootStrap 类加载器
public static boolean isSystemDomainLoader(ClassLoader var0) {
    return var0 == null;
}

判断是不是 BootStrap 类加载器加载的 var0,在这里是看是不是 BootStrap 加载器加载了 TestUnSafe.class 。由于 TestUnSafe.class 是使用 AppClassLoader 加载的,所以这里抛出了异常。

为何要进行这个判断?

UnSafe 类是 rt.jar 包提供的,rt.jar 包里面的类是使用 BootStrap 类加载的,而main 函数所在的类是使用 AppClassLoader 加载的,所以在 mian 函数里面加载 UnSafe 类时,根据委托机制,会委托给 BootStrap 去加载 UnSafe 类。
如果没有限制,那么我们的应用程序就可以随意使用 UnSafe 做事情,而 UnSafe 类可以直接操作内存,这是不安全的,所以 JDK 开发组特意做了这个限制,让开发人员在正规渠道使用 UnSafe 类,而是在 rt.jar 包里面的核心类中使用 UnSafe功能。

实例化 UnSafe 类的方法

public class TestUnSafe2 {
    /**
     * 获取 UnSafe 对象
     */
    static final Unsafe unsafe;

    /**
     * 记录变量 state 在类 TestUnsafe 中的偏移量
     */
    static final long stateOffset;

    /**
     * 变量
     */
    private volatile long state = 0;

    static {
        try {
            //使用反射获取 Unsafe 的成员变量 theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");

            //设置为可存取
            field.setAccessible(true);

            //获取该变量的值
            unsafe = (Unsafe) field.get(null);

            //获取 state 变量在类 TestUnsafe 中的偏移量
            stateOffset = unsafe.objectFieldOffset(TestUnsafe.class.getDeclaredField("state"));
        } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
            throw new Error(e);
        }
    }

    public static void main(String[] args) {
        TestUnSafe2 test = new TestUnSafe2();
        boolean success = unsafe.compareAndSwapInt(test, stateOffset, 0, 1);
        System.out.println(success); //true
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值