Java8 Unsafe 解开你神秘的面纱

前言

Unsafe 类一直是个很神秘的角色,我们普通开发者几乎不会碰到,顶多也是使用了并发包之类的系统类库,间接使用到了而已。那它到底是用来做什么的呢?它提供了我们直接操作内存的接口。Java 本身作为一个内存自动管理的工具,内存的开辟释放由虚拟机代为管理,然而,HotSpot 的设计者留下了 Unsafe 的类,用于扩展,它可以直接开辟内存,释放内存,读取任意地址的内存,而不受 Java 堆内存的限制。尽管如此,它并不能为我们所用,加载这个类只能由系统的类加载器执行,但我们可以通过反射获取到它的实例对象,Java 反射的确很牛啊。

如何获取实例对象

第一个问题:为什么我们需要通过 Field 获取,不能使用 newInstance 获取呢?
通过 newInstance 的方法获取实力需要构造函数是 public 的,否则会抛异常,及时 getUnsafe 是静态函数,我们也不能通过这个去获取,因为这时候类加载不是系统的,会抛异常

    private Unsafe() {
    }

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }
        Unsafe unsafe = Unsafe.class.newInstance();

由于 Unsafe 是单例,当类加载时,theUnsafe 实例会被加载,这样我们就可以通过反射获取这个实例

    static {
        registerNatives();
        Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
        // 重点
        theUnsafe = new Unsafe();
    }

通过 Field 获取

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
    }

获取到这实例,就可以干很多事情了.....
先来看看 API 图


Screen Shot 2018-09-16 at 11.20.42 PM.png
Screen Shot 2018-09-16 at 11.20.59 PM.png
Screen Shot 2018-09-16 at 11.21.15 PM.png

太多了,眼睛都看瞎了
归个类吧

  • 直接操作内存,将某个对象的值更改,读取,比如将 String 的 value 更改,是不是很神奇,这个是用反射也可以更改哦!下次面试的时候可以考考别人 String 的 value 怎么才能改的掉,看他会几种。
        String str = "Hello Unsafe";
        Field value = str.getClass().getDeclaredField("value");
        unsafe.putObject(str, unsafe.objectFieldOffset(value), new char[]{'M', 'a', 'j', 'i', 'c'});]
        System.out.println(str);
        // output: Majic

        // throw exception
        String str = "Hello Unsafe";
        Field value = str.getClass().getDeclaredField("value");

        value.setAccessible(true);
        value.set(str, new char[] {'f', 'i', 'n', 'a', 'l'});
  • CompareAndSwap 著名的 CAS
    为了保证并发安全, CAS 涉及到的变量应该使用 volatile 修饰,保证读到的值最新
    allocateInstance 新建一个没有初始化的实例,各个值都是默认值
    compareAndSwapLong 第一个参数是 object,第二个参数是变量内存偏移值,可以用 unsafe 类获取实际偏移值,第三个参数是 期望值,第四个三叔目标值,大致的语义就是:如果内存中是期望值,我就更新为目标值,否则不更新
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
        InnerClass o = (InnerClass)unsafe.allocateInstance(InnerClass.class);
        o.print(); // print 100
        Field a = o.getClass().getDeclaredField("value");
        unsafe.putLong(o, unsafe.objectFieldOffset(a), 10000);
        o.print(); // print 10000
        unsafe.compareAndSwapLong(o, unsafe.objectFieldOffset(a), 10000, 1111);
        o.print(); // print 1111
        unsafe.compareAndSwapLong(o, unsafe.objectFieldOffset(a), 1000, 10000);
        o.print(); // print 1111
    }
     
    static class InnerClass {
        // 保证内存可见性
        private volatile long value;
        InnerClass() {
            value = 100L;
        }
        void print() {
            System.err.println("value==>" + value);
        }
    }
  • 线程挂起,取消
    使用 unsafe.unpark 可以取消 Thread.sleep() 后者 park 的线程
    使用 unsafe.park(false, 1000000000) 可以挂起当前线程,第一个参数表示是isAbsolute,是否是绝对时间,后一个参数为时间,flase 表示相对时间,0表示一直挂起,单位为纳秒;true 表示绝对时间,单位为毫秒,System.currentThreadMillis 搭配使用
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, InterruptedException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);

        InnerThread innerThread = new InnerThread(unsafe);
        innerThread.run();

        Thread.sleep(20);
        // 取消挂起
        unsafe.unpark(innerThread);
    }

    static class InnerThread extends Thread {
        Unsafe unsafe;

        InnerThread(Unsafe unsafe) {
            this.unsafe = unsafe;
        }

        @Override
        public void run() {
            System.out.println("start");
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //unsafe.park(false, 1000000000L);
            System.out.println("end");
        }
    }

小结

以后又可以吹一波了,一箭双雕,反射,Unsafe

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容