Unsafe类的源码解读以及使用场景

扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,阅读更多Spring源码分析Java并发编程文章。

微信公众号

  在上一篇文章《初始CAS的实现原理》中,提到了Unsafe类相关方法,今天这篇文章将详细介绍Unsafe类的源码。
  为什么要单独用一篇文章介绍Unsafe类呢?这是因为在看源码过程中,经常会碰到它,例如JUC包下的原子类、AQS、Netty等源码中,最终都会看见Unsafe类的使用。搞清楚Unsafe类的使用,对以后看源码会有很大的帮助。

1. Unsafe类简介

  • Unsafe类是rt.jarsun.misc包下的类,从类名就能看出来,这个类是不安全的,但是它的功能十分强大。相比C和C++的开发人员,作为一名Java开发人员是十分幸福的,因为在Java中程序员在开发时不需要关注内存的管理,对象的回收,因为JVM全部都帮助我们完成了。如果Java开发人员需要自己手动去操作内存,那么可以通过Unsafe类去进行申请,这也是Unsafe类被定义为不安全的类的原因,因为一不小心就容易出现忘记释放内存等问题。
  • Unsafe类中方法很多,但大致可以分为8大类。CAS操作、内存操作、线程调度、数组相关、对象相关操作、Class相关操作、内存屏障相关、系统相关。笔者画了一张脑图,因为图片占用空间较大,为了不影响阅读,我把这张图放在了文章末尾,以供参考。

2. 如何获取Unsafe类的实例

  • Unsafe类被final修饰了,表示Unsafe不能被继承;同时Unsafe的构造方法用private修饰,表示外部无法直接通过构造方法去创建实例。实际上Unsafe是一个单例对象,下面是Unsafe类的部分源码。
// 类被final修饰,表示不能被继承
public final class Unsafe {
   

	// 构造器被私有化
    private Unsafe() {
   }

    private static final Unsafe theUnsafe = new Unsafe();

    public static Unsafe getUnsafe() {
   
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }
}
  • 虽然Unsafe是一个单例,但是我们在自己开发的类中无法通过Unsafe.getUnsafe()获取到Unsafe的实例,在程序运行时会抛出SecurityException异常。例如如下示例:
public class Demo {
   

    public static void main(String[] args) {
   
        Unsafe unsafe = Unsafe.getUnsafe();
    }
}
  • 运行main()方法,最终在控制台出现如下运行时异常:
Exception in thread "main" java.lang.SecurityException: Unsafe
    at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
    at com.tiantang.study.Demo.main(Demo.java:14)
  • 为什么会出现SecurityException异常呢?这是因为在Unsafe类的getUnsafe()方法中,它做了一层校验,判断当前类(Demo)的类加载器(ClassLoader)是不是启动类加载器(Bootstrap ClassLoader),如果不是,则会抛出SecurityException异常。在JVM的类加载机制中,自定义的类使用的类加载器是应用程序类加载器(Application ClassLoader),所以这个时候校验失败,会抛出异常。
  • 那么如何才能获取到Unsafe类的实例呢?有两种方案。
  • 第一方案:将我们自定义的类(如Demo类)所在的jar包所在的路径通过-Xbootclasspath参数添加到Java命令中,这样当程序启动时,Bootstrap ClassLoader会加载Demo类,这样校验就通过了。显然这种方式比较麻烦,而且不太实用,因为在项目中,可能需要在很多地方都使用Unsafe类,如果通过Java命令行这种方式去指定,就会很麻烦,而且容易出现纰漏。
  • 第二种方案:通过反射来创建Unsafe类的实例(反射反射,程序员的快乐)。反射的代码可以参考如下示例:
public static void main(String[] args) {
   
    try {
   
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        // 将字段的访问权限设置为true
        field.setAccessible(true);
        // 因为theUnsafe字段在Unsafe类中是一个静态字段,所以通过Field.get()获取字段值时,可以传null获取
        Unsafe unsafe = (Unsafe) 
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值