Unsafe的使用解析

相信很多人都阅读过jdk源码,这两天在看一段代码都时候正好用到Unsafe(JRE的rt.jar中提供了一个类sun.misc.Unsafe),特地研究了下Unsafe这个类。基于对象内存来进行操作对象。不安全。所以不建议使用。特别在concurrent包中都Atomicxxx类,底层都CAS 都用到了这个类。那么这个类究竟能干什么,我们先从实例化开始:

  • 首先,这个类是不能直接实例化到,但是可以通过反射来获取。
        sun.misc.Unsafe U = null;

        Field theUnsafeInstance = null;
        try {
            theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafeInstance.setAccessible(true);
            U = (Unsafe) theUnsafeInstance.get(Unsafe.class);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
  • 其次,我们一般会用到这个类到compareAndSwapXXX,这个方法是干嘛用的?官网解释如下:
    Screen Shot 2018-12-10 at 9.43.36 PM.png

    就是根据对象在内存的offset(我们叫他偏移量),来比较你期望的值和内存值是否相等,如果相等返回true,并进行swap,用最后一个x来进行swap。

  • 那么,我们来演示下:
    建一个对象:

public class Main {


    private String s;
    private long s1;
    private long s2;

    public static void main(String[] args) {
        Main main = new Main();
        main.s1 = 20;
        long s, next;
        long state = 256;
        System.out.println(1 << 6);
        System.out.println(1 << 10);
        System.out.println(1L << 7);
        System.out.println("Hello World!");
        long except = 1;
        long real = 1;
        sun.misc.Unsafe U = null;

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

        System.out.println("========");

//        Arrays.stream(Main.class.getDeclaredFields()).forEach(field -> System.out.println(U.objectFieldOffset(field.getName())));

        Field[] fields = Main.class.getDeclaredFields();
        for(Field field : fields) {

            System.out.println(field.getName()+ "=="+ U.objectFieldOffset(field));
        }


        System.out.println("========");


        boolean flag = U.compareAndSwapLong(main,16,21,21);
        System.out.println(flag);
        System.out.println(main.s1);

    }
}
  • 我们看到了这个代码,有三个field。如何获得这三个field?如下这段代码发挥作用:
Field[] fields = Main.class.getDeclaredFields();
  • 获取对象的偏移量:
        for(Field field : fields) {

            System.out.println(field.getName()+ "=="+ U.objectFieldOffset(field));
        }

打印出来的结果如下:

s==12
s1==16
s2==24

这个数字就是offset。那么我们可以验证这个compare方法了。

  • 验证compare方法:我们将offset为16的内存变量s1改成21(原先是20)
boolean flag = U.compareAndSwapLong(main,16,20,21);
System.out.println(flag);
System.out.println(main.s1);

就是比较偏移量为16的内存对象值是否是20,如果是,改成21,打印结果如下:

true
21

可以看到,如预期。好了,大家都知道类似的方法怎么用了吧。期望对大家有帮助。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容