前言

我们经常在JUC包下的ConcurrentHashMap、Atomic开头的原子操作类、AQS以及LockSupport里面看到Unsafe类的身影,这个Unsafe类究竟是干什么的,本文可以带着读者一探究竟。

Java和C++、C语言的一个重要区别,就是Java中我们无法直接操作一块内存区域,而在C++、C中却可以自己申请内存和释放内存。Unsafe类的设计,为我们提供了手动管理内存的能力。

如同它的名字一样,它被认定为不安全的。直接操纵内存,意味着实例化出来的对象不会受到JVM的管理,不会被GC,需要手动进行回收,容易出现内存泄露的问题。因此,官方并不建议我们在自己的应用程序中使用该类。


构造方法

public final class Unsafe {private static final Unsafe theUnsafe;private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");} else {return theUnsafe;}}//其他方法
}

可以看得出来,该类被final修饰,不允许被继承。构造方法是私有的,在外部不可被实例化。(关于final更多的作用,可以移步这篇文章关键词final的作用)

但在内部提供了一个获取单例的getUnsafe()方法,不过该方法做了限制。如果是普通调用的话,它会抛出一个SecurityException异常。只有由系统类加载器(BootStrap classLoader)加载的类才可以调用这个类中的方法。

如果var0由系统类加载器加载的话,那么var0.getClassLoader()会返回null,VM.isSystemDomainLoader(null)则直接返回true,此时便不会抛出SecurityException异常。

当然,也不是无法获取到Unsafe类的实例,我们在文章最后会通过反射来获取。


获取偏移量

    public native long staticFieldOffset(Field var1);public native long objectFieldOffset(Field var1);
  • staticFieldOffset用于获取某一个静态属性在对象地址中的偏移量
  • objectFieldOffset用于获取某一个非静态属性在对象实例地址中的偏移量

偏移量这个名词在Unsafe类中十分重要,该类中80%的方法都需要依赖这个偏移量。


分配、释放内存等

    //分配内存public native long allocateMemory(long var1);//扩展或重新分配内存public native long reallocateMemory(long var1, long var3);//内存初始化public native void setMemory(Object var1, long var2, long var4, byte var6);//内存复制public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);//释放内存public native void freeMemory(long var1);//创建对象实例并返回该实例public native Object allocateInstance(Class<?> var1) throws InstantiationException;

其中allocateMemory()、reallocateMemory()、freeMemory()分别用于分配内存,扩展或重新分配内存和释放内存,与C语言中 malloc()、realloc()、free()对应。

allocateInstance()方法会通过Class对象创建一个类的实例,且不需要调用其构造函数、初始化代码、JVM安全检查等等。


普通读写

    public native int getInt(long var1);public native void putInt(long var1, int var3);
  • getInt()   在指定内存地址var1处读取一个int
  • putInt()   在指定内存地址var1处写入一个新int类型的数据var3

普通的读写,无法保证有序性与可见性。关于可见性,可以先移步到我的另外一篇文章多线程之内存可见性


volatile读写

    public native int getIntVolatile(Object var1, long var2);public native void putIntVolatile(Object var1, long var2, int var4);
  • getIntVolatile   在对象var1的指定偏移处var2读取一个int
  • putIntVolatie    在对象var1的指定偏移处var2写入一个int类型的数据var4

volatie能够保证有序性以及可见性,volatie保证有序性的一个实例,可以参考我的另外一篇文章浅说Synchronized中使用synchronized与volatie实现单例模式中双重检验锁的部分。


CAS操作

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

以compareAndSwapInt为例,在对象var1指定偏移量var2处读取一个int值real,如果var4=real,则用var5更新这个real,并返回true,否则返回false。

CAS用于实现乐观锁,每次更新时,都假设不会有其他线程并发修改,而只是在修改的时候判断该值是否被修改过。如果符合预期的值,则直接更新它,否则进入忙循环中,一直判断是否符合期望。在循环次数少便可以直接更新值的情况下,CAS机制比悲观锁拥有更好的性能。当然,如果循环次数过多,也是会白白浪费CPU资源。

synchronized就是一中悲观锁的实现,关于synchronized原理,可以移步我的另外一篇文章Synchronized的优化

关于CAS机制的详细介绍,我会另开篇幅。


线程调度

    public native void park(boolean var1, long var2);public native void unpark(Object var1);
  • park()        用于阻塞当前线程,如果var1=true,则var2的单位为毫秒,否则为纳秒。
  • unpark()    用于恢复一个之前被park的线程var1

LockSupport里面的park()与unpark()方法内部正是通过以上两个本地方法来实现的。


内存屏障

    public native void loadFence();public native void storeFence();public native void fullFence();
  • loadFence      保证在这个屏障之前,所有的读操作全部完成。这里的load对应于从局部变量表中读取某个位置上的元素到栈顶
  • storeFence     保证在这个屏障之前,所有的写操作全部完成。这里的store对应于从栈顶出栈某个元素保存到局部变量表的某个位置上
  • fullFence        保证在这个屏障之前,所有的读写操作全部完成,相当于load+store

反射获取Unsafe实例

package com.yang.testUnsafe;import sun.misc.Unsafe;import java.lang.reflect.Field;public class Main {static class Student {private String name;private int age;public Student() {System.out.println("通过构造方法");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {//1.获取Unsafe类的实例//但无法通过Unsafe.getUnsafe(),由于Main类不是系统类加载器加载,因此会抛出SecurityException异常//Unsafe unsafe = Unsafe.getUnsafe();Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);//2.为Student对象分配内存//该方法不需要调用构造方法Student student = (Student) unsafe.allocateInstance(Student.class);//3.获取student实例中age属性的偏移量Field age = student.getClass().getDeclaredField("age");long ageOffset = unsafe.objectFieldOffset(age);//4.利用CAS操作,当age=0时,将age变为1boolean casResult = unsafe.compareAndSwapInt(student, ageOffset, 0, 1);System.out.println(casResult);//输出trueSystem.out.println(student.getAge());//输出1}
}

总结

使用Unsafe类直接操纵内存,意味着速度更快,效率更高,但也更加危险。之前盛传Unsafe类将在Java9中移除,一时间风波四起,具体的文章可以参考这篇Java 9中将移除 Sun.misc.Unsafe(译)。

但其实Java9出现之后,只是对其进行了改进和优化,不过依然是不推荐开发者使用Unsafe类。

JUC基石——Unsafe类相关推荐

  1. 【高并发】JUC底层工具类Unsafe

    1.概述 转载:添加链接描述 参考:[java]java的unsafe 参考:JUC原子类: CAS, Unsafe和原子类详解 1.1 本文主要内容 Unsafe基本介绍 获取Unsafe实例 Un ...

  2. 腾讯架构师理解的并发编程基石——Thread类的工作原理

    1. 开篇词 说到并发编程,可能大家脑海中的第一印象会是 Thread.多线程.JUC.线程池.ThreadLocal 等等内容.确实,并发编程是 Java 编程中不可或缺的一部分,掌握并发编程的核心 ...

  3. Java双刃剑之Unsafe类详解

    前一段时间在研究juc源码的时候,发现在很多工具类中都调用了一个Unsafe类中的方法,出于好奇就想要研究一下这个类到底有什么作用,于是先查阅了一些资料,一查不要紧,很多资料中对Unsafe的态度都是 ...

  4. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  5. 14.JDK底层Unsafe类是个啥东西?

    老王:小陈啊,从今天开始我们就要进入<结丹篇>了,在这一篇章里面,要注意听讲啊,对后面的每一个阶段的理解来说都至关重要的...... 小陈:好的,老王,前面的<筑基>.< ...

  6. 15.unsafe类的CAS是怎么保证原子性的?

    老王:小陈啊,上一章我们讲了usafe是个啥东西,以及unsafe提供的几大类的功能 老王:这一章啊,我们要花个时间专门讲unsafe提供的cas功能,这个cas的功能是我们后面将Atomic原子类体 ...

  7. 初识sun.msic.Unsafe类:CAS操作的核心类

    java不能直接访问操作系统底层,而是通过native本地方法来访问.Unsafe类提供了硬件级别的原子操作 juc并发包,即java.util.concurrent包,是JDK的核心工具包,是JDK ...

  8. Unsafe 类详解

    文章转摘自:https://www.cnblogs.com/throwable/p/9139947.html 原作者: throwable Unsafe介绍 在Oracle的Jdk8无法获取到sun. ...

  9. JDK源码——UnSafe类

    一.摘要 juc中大部分类都是依赖于Unsafe来实现的,主要用到了Unsafe中的CAS.线程挂起.线程恢复等相关功能.所以如果打算深入了解JUC原理的,必须先了解一下Unsafe类. Unsafe ...

最新文章

  1. Android library module生成aar文件
  2. 提升网站竞争力从这三方面着手努力!
  3. 【采用】反欺诈之四大杀器
  4. 前深度学习时代CTR预估模型的演化之路 [王喆观点]
  5. Tensorflow实现多元线性回归
  6. unity平行光太亮?物体发白?可能你使用了2个或多个平行光
  7. 小红书回应泄露未成年人隐私及审核漏放
  8. 单片机 c语言 宏程序,宏程序学习的几点心得.doc
  9. Apple watch无法登陆网易云音乐
  10. 获取windows7 trustedInstaller权限
  11. MySQL中EXPLAIN解析
  12. PS快速美白磨皮方法
  13. java自动往数据库里插shuaku_x大x鸟的青鸟云课堂自动答题实现原理
  14. 21种优化产品转化率的设计技巧
  15. Nature Communications:基于弥散张量成像的人类纤维束连接体方法面临的挑战
  16. iOS ZBarSDK 用ZBarReaderView自定义二维码扫描界面
  17. JavaIO知识简述
  18. 分布式电商项目 谷粒商城 学习笔记<2>
  19. html+css+javascript满屏雪花爱心520表白网站 (含音乐)520告白/七夕情人节/生日礼物/程序员表白必备...
  20. 一代信仰落幕,凯迪拉克CTS-V已经停产

热门文章

  1. [小黄书小程序]主页面标签栏水平滑动和下拉弹出框
  2. ZYNQ初体验千兆以太网的那些事儿(ps端)
  3. 项目经理应该怎么做,才能提升团队的执行力?
  4. 改变美妙人生的100个经典句子
  5. 程序员--看看你的反应有多快,有趣的脑筋急转弯
  6. 选择精密导电滑环时需要注意什么?
  7. unity的垂直同步VSync
  8. mssql for php 安装 适用于 mssql 2005
  9. 从简历被拒到收割今日头条Offer
  10. php牛逼的面试题分享