我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java中Unsafe怎么用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java中Unsafe怎么用

这篇文章将为大家详细讲解有关Java中Unsafe怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

Unsafe是什么

首先我们说Unsafe类位于rt.jar里面sun.misc包下面,Unsafe翻译过来是不安全的,这倒不是说这个类是不安全的,而是说开发人员使用Unsafe是不安全的,也就是不推荐开发人员直接使用Unsafe。而且Oracle JDK源码包里面是没有Unsafe的源码的。其实JUC包里面的类大部分都用到了Unsafe,可以说Unasfe是java并发包的基石。

如何正确地获取Unsafe对象

我们从源码中看如何获取Unsafe对象

private Unsafe() {}

首先构造方法私有化,这就说明我们不能通过new Unsafe的方式创建Unsafe对象。

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

我又发现有一个静态方法,方法名为getUnsafe,而且返回值为Unsafe,看来调用这个方法可以获取Unsafe对象。

为此我又编写了如下代码测试这样是否行得通

public static void main(String[] args) throws Exception{        Unsafe unsafe = Unsafe.getUnsafe();        System.out.println(unsafe);}

谁知道控制台竟然报错了

Java中Unsafe怎么用

看错误提示信息是权限方面的错误,但是我看AtomicBoolean类获取Unsafe的方式就是调用getUnsafe方法,可能是只允许JDK内部的类可以通过这种方式访问吧,这里我们不深究,再想别的办法获取。

继续看源码找突破口

Unsafe类里面第一个常量是 private static final Unsafe theUnsafe; 用static和final修饰而且没有直接赋值,这就说明肯定有静态代码块对theUnsafe赋值了,然后再类的底部发现了。

static {        registerNatives();        Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});        theUnsafe = new Unsafe();        ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);        ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);        ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset(short[].class);        ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset(char[].class);        ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class);        ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset(long[].class);        ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset(float[].class);        ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset(double[].class);        ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset(Object[].class);        ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);        ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale(byte[].class);        ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale(short[].class);        ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale(char[].class);        ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class);        ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale(long[].class);        ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale(float[].class);        ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale(double[].class);        ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale(Object[].class);        ADDRESS_SIZE = theUnsafe.addressSize();    }

第四行对theUnsafe进行了赋值。也就是说在类加载完成后Unsafe里面的theUnsafe常量就已经赋值好了Unsafe对象,如果我们想获取Unsafe对象只要用反射拿到theUnsafe属性就可以了。

    public static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {        Field field = Unsafe.class.getDeclaredField("theUnsafe");        //私有属性可以访问        field.setAccessible(true);        Unsafe unsafe = (Unsafe) field.get(null);        System.out.println(unsafe);        return unsafe;    }

Java中Unsafe怎么用

Unsafe实现CAS锁

CAS是compare and swap的缩写,中文翻译成比较并交换。 在juc包下Atomic开头的类都是使用的CAS锁实现的并发条件下对一个变量赋值不覆盖的。我们也可以自己使用Unsafe实现CAS锁。

 interface Counter{        void increment();        long getCounter();    }     static class CasCounter implements Counter{        private volatile long counter = 0;        private Unsafe unsafe;        private long offset;        CasCounter() throws NoSuchFieldException, IllegalAccessException {            unsafe = getUnsafe();            //获得该类counter属性内存偏移量起始位置            offset = unsafe.objectFieldOffset(CasCounter.class.getDeclaredField("counter"));        }        @Override        public void increment() {            long current = counter;            //循环判断是否赋值成功  第一个参数为调用本方法的对象,第二个参数为要更改属性的内存偏移量,第三个参数是未修改之前的值,第四个参数是想要修改为那个值。            while (!unsafe.compareAndSwapLong(this,offset,current,current+1)){                current = counter;            }        }        @Override        public long getCounter() {            return counter;        }    }

这里我们主要看unsafe.objectFieldOffset(CasCounter.class.getDeclaredField("counter"));这一行代码,因为通过CAS对属性值进行更改是直接在内存上进行更改的,所以我们需要拿到这个对象的counter属性的内存偏移量。

再看increment方法,这里我们在更改之前先拿到counter的值,unsafe.compareAndSwapLong方法就是根据内存偏移量进行更改值的,第一个参数确定那个对象,第二个参数确定那个属性,第三个参数比对要更改属性的原值,第四个参数要更改的值。如果更改成功则返回true,更改失败返回false。这里的逻辑是更改失败就一直更改,知道更改成功才跳出循环。这样就会有效的防止属性值被覆盖的问题。 我们写的CasCounter类就实现了AtomicInteger类的部分功能。

使用Unsafe创建对象

我们都知道反射可以‘走后门'创建对象,其实Unsafe也是可以的

static class Simple{        static {            System.out.println("类初始化");        }        private long l = 0;        public Simple(){            this.l = 1;            System.out.println("对象初始化");        }        public long get(){            return l;        }} public static void main(String[] args) throws Exception {      Unsafe unsafe = getUnsafe();        //相当于直接在内存中开辟一块地址,不运行构造方法        Simple simple = (Simple) unsafe.allocateInstance(Simple.class);        System.out.println("通过unsafe创建对象不会运行构造方法: " + simple.get());        System.out.println("但是可以通过对象获得class对象" + simple.getClass());        System.out.println("也可以拿到类加载器 " + simple.getClass().getClassLoader()); }

控制台输出如下

Java中Unsafe怎么用

这里我们发现使用Unsafe创建对象并没有运行构造方法,而只是将对象创建出来了。而使用反射创建对象是会运行构造方法的和使用new的方式创建对象别无二致。所以不推荐使用Unsafe创建对象。

Unsafe加载类

既然Unsafe是直接操作的内存那应该也可以加载类,下面我们看看Unsafe是如何加载类的。

我们先自己编写A类

public class A{ private int i = 0;  public A(){  this.i = 10; } public int get(){  return i; }}

然后运行javac A.java 生成A.class此时A.class的位置是F:\tmp

其次我们编写Unsafe加载class的代码

     public static byte[] loadClassContent() throws IOException {        File file = new File("F:\\tmp\\a.class");        FileInputStream fis = new FileInputStream(file);        byte[] content = new byte[(int) file.length()];        fis.read(content);        fis.close();        return content;    }    public static void main(String[] args) throws Exception {        Unsafe unsafe = getUnsafe();        byte[] bytes = loadClassContent();        Class<?> aClass = unsafe.defineClass(null, bytes, 0, bytes.length,null,null);        Method get = aClass.getMethod("get");        int i = (int) get.invoke(aClass.newInstance(), null);        System.out.println(i);    }

这里unsafe.defineClass方法就是加载类的方法。

运行后输出结果为10

这样我们就实现了通过Unsafe加载类。

Unsafe更改私有属性值

我们都知道反射可以更改对象私有属性值,其实Unsafe也可以直接更改私有属性值,代码如下

static class Guard{        private int ACCESS_ALLOWED = 1;        private boolean allow(){            return 42==ACCESS_ALLOWED;        }        public void work(){            if (allow()){                System.out.println("你进行了暗箱操作");            }        } }public static void main(String[] args) throws Exception {        Unsafe unsafe = getUnsafe();        Guard guard = new Guard();        Field access_allowed = guard.getClass().getDeclaredField("ACCESS_ALLOWED");        unsafe.putInt(guard,unsafe.objectFieldOffset(access_allowed),42);        guard.work();}

输出结果为 你进行了暗箱操作 ,putInt方法第一个参数是要更改的属性属于哪个对象,第二个参数是要更改属性的内存偏移量,第三个参数是要改成什么值。其实就是直接更改指定内存地址中的int属性的值。这样我们就完成了使用Unsafe更改对象私有属性值。

Unsafe类能直接操作内存的特性决定了它能走太多的后门了,而且大部分方法都是native修饰的,底层调用的C++。估计这也是Unsafe的不安全的原因。

关于“Java中Unsafe怎么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java中Unsafe怎么用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java中Unsafe怎么用

这篇文章将为大家详细讲解有关Java中Unsafe怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Unsafe是什么首先我们说Unsafe类位于rt.jar里面sun.misc包下面,Unsafe翻译
2023-06-25

java中Unsafe类怎么用

这篇文章主要介绍了java中Unsafe类怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了
2023-06-28

Java中Unsafe类有什么用

小编给大家分享一下Java中Unsafe类有什么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、Unsafe类是啥?Java最初被设计为一种安全的受控环境。尽
2023-06-21

Java中Unsafe如何使用

Java中Unsafe如何使用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Unsafe介绍Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安
2023-06-15

Go语言中unsafe包怎么使用

本文小编为大家详细介绍“Go语言中unsafe包怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Go语言中unsafe包怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. 什么是unsafe包un
2023-07-05

使用Java怎么实现一个CAS和Unsafe类

本篇文章给大家分享的是有关使用Java怎么实现一个CAS和Unsafe类,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。两者对比sychronized和volatile都解决了内
2023-06-06

Golang中的unsafe包有什么用

今天小编给大家分享一下Golang中的unsafe包有什么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。unsafe 包提
2023-07-05

Java中Unsafe在安全领域的使用方法

本文小编为大家详细介绍“Java中Unsafe在安全领域的使用方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java中Unsafe在安全领域的使用方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。0 前言u
2023-06-29

【基本功】Java魔法类:Unsafe应用解析

大家好,我是程序员鼓励师美美~《基本功》专栏又上新了:Java中的Unsafe类在提升运行效率、增强底层资源操作能力方面有很大的用处。但如果在开发过程中使用不当,就会出现各种“莫名其妙”的问题。本篇文章将会带领你重新认识它,绕过“开发雷区”
2023-06-02

深入探索Go语言中unsafe包的使用

Go语言的unsafe包被誉为黑科技,它为Go语言提供了底层访问和操控内存的能力,本文将深入探讨Go语言中unsafe包的使用方法和注意事项,需要的可以参考一下
2023-05-14

sun unsafe类功能及使用注意事项是什么

这篇文章跟大家分析一下“sun unsafe类功能及使用注意事项是什么”。内容详细易懂,对“sun unsafe类功能及使用注意事项是什么”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入
2023-06-28

Java中Feign怎么用

这篇文章给大家分享的是有关Java中Feign怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一,简介Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit、JAXRS-
2023-05-30

java中lock怎么用

小编给大家分享一下java中lock怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编
2023-06-14

java中ThreadLocal怎么用

这篇文章将为大家详细讲解有关java中ThreadLocal怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。package test;public class ThreadLocalTest {pub
2023-06-02

java中indexof()怎么用

这篇文章主要介绍java中indexof()怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!IndexOf 方法返回 String 对象内第一次出现子字符串的字符位置。strObj.indexOf(subStri
2023-06-03

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录