文章目录

  • Unsafe
  • Unsafe 提供的几个主要的方法
    • long objectFieldOffset(Field field)
    • int arrayBaseOffset(Class arrayClass)
    • int arrayIndexScale(Class arrayClass)
    • boolean compareAndSwapLong(Object obj, long offset, long expect, long update)
    • public native long getLongvolatile(Object obj, long offset)
    • void putLongvolatile(Object obj, long offset, long value)
    • void putOrderedLong(Object obj, long offset, long value)
    • void park(boolean isAbsolute, long time)
    • void unpark(Object thread)
    • long getAndSetLong(Object obj, long offset, long update)
    • long getAndAddLong(Object obj, long offset, long addValue)
  • 使用Unsafe类


Unsafe

JDK 的 rt.jar 包中的 Unsafe 类提供了硬件级别的原子性操作,Unsafe 类中的方法都是native 方法,它们使用 JNI 的方式访问本地 C++ 实现库。

Unsafe 提供的几个主要的方法

下面我们来了解一下 Unsafe 提供的几个主要的方法以及编程时如何使用 Unsafe 类做一些事情。

long objectFieldOffset(Field field)

返回指定的变量在所属类中的内存偏移地址,该偏移地址仅仅在该 Unsafe 函数中访问指定字段时使用。

如下代码使用 Unsafe 类获取变量 value 在 AtomicLong 对象中的内存偏移


int arrayBaseOffset(Class arrayClass)

获取数组中第一个元素的地址


int arrayIndexScale(Class arrayClass)

获取数组中一个元素占用的字节


boolean compareAndSwapLong(Object obj, long offset, long expect, long update)

比较对象obj中偏移量为offset的变量的值是否与expect相等,相等则使用update值更新,然后返回true,否则返回false


public native long getLongvolatile(Object obj, long offset)

获取对象obj中偏移量为offset的变量对应volatile语义的值


void putLongvolatile(Object obj, long offset, long value)

设置obj对象中offset偏移的类型为long的field的值为value,支持volatile语义


void putOrderedLong(Object obj, long offset, long value)

设置obj对象中offset偏移地址对应的long型field的值为value。这是一个有延迟的putLongvolatile方法,并且不保证值修改对其他线程立刻可见。只有在变量使用volatile修饰并且预计会被意外修改时才使用该方法。


void park(boolean isAbsolute, long time)

阻塞当前线程,其中参数isAbsolute等于false且time等于0表示一直阻塞。time大于0表示等待指定的time后阻塞线程会被唤醒,这个time是个相对值,是个增量值,也就是相对当前时间累加time后当前线程就会被唤醒。

如果isAbsolute等于true,并且time大于0,则表示阻塞的线程到指定的时间点后会被唤醒,这里time是个绝对时间,是将某个时间点换算为ms后的值。

另外,当其他线程调用了当前阻塞线程的interrupt方法而中断了当前线程时,当前线程也会返回,而当其他线程调用了unPark方法并且把当前线程作为参数时当前线程也会返回。


void unpark(Object thread)

唤醒调用park后阻塞的线程


下面是JDK8新增的函数,这里只列出Long类型操作。

long getAndSetLong(Object obj, long offset, long update)

获取对象obj中偏移量为offset的变量volatile语义的当前值,并设置变量volatile语义的值为update

由以上代码可知,首先(1)处的getLongvolatile获取当前变量的值,然后使用CAS原子操作设置新值。这里使用while循环是考虑到,在多个线程同时调用的情况下CAS失败时需要重试。


long getAndAddLong(Object obj, long offset, long addValue)

获取对象obj中偏移量为offset的变量volatile语义的当前值,并设置变量值为原始值+addValue

类似getAndSetLong的实现,只是这里进行CAS操作时使用了原始值+传递的增量参数addValue的值。

使用Unsafe类

Unsafe 这个类如此厉害,试一试???


import sun.misc.Unsafe;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/11/28 20:05* @mark: show me the code , change the world*/
public class TestUnSafe {// 1 获取 Unsafe的实例static final Unsafe unsafe = Unsafe.getUnsafe();// 2 记录变量 state在类 Testunsafe中的偏移值static final long stateoffset;// 3 变量private volatile long state = 0;static {try {// 4 获取state变量在类TestUnsafe中的偏移量stateoffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state"));} catch (Exception e) {System.out.println(e.getLocalizedMessage());throw new Error(e);}}public static void main(String[] args) {// 5. 创建实例TestUnSafe testUnSafe = new TestUnSafe();// 6 设置state值为1 通过cas算法boolean b = unsafe.compareAndSwapLong(testUnSafe, stateoffset, 0, 1);System.out.println(b);}}
  • 代码1 获取了Unsafe的一个实例

  • 代码3创建了一个变量state并初始化为0。

  • 代码4使用unsafe.objectFieldOffset获取TestUnSafe类里面的state变量,在TestUnSafe对象里面的内存偏移量地址并将其保存到stateOffset变量中。

  • 代码6 调用创建的unsafe实例的compareAndSwapInt方法,设置test对象的state变量的值。具体意思是,如果test对象中内存偏移量为stateOffset的state变量的值为0,则更新该值为1。

运行上面的代码,我们期望输出true,然而执行后会输出如下结果



看看getUnsafe的代码吧

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

继续看下

    //  9 判断是不是BootStrap类加载器加载的 public static boolean isSystemDomainLoader(ClassLoader var0) {return var0 == null;}
  • 代码7获取调用getUnsafe这个方法的对象的Class对象,这里是TestUnSafe.class。

  • 代码8判断是不是Bootstrap类加载器加载的localClass,在这里是看是不是Bootstrap加载器加载了TestUnSafe.class。很明显由于TestUnSafe.class是使用AppClassLoader加载的,所以这里直接抛出了异常。

思考一下,这里为何要有这个判断? 我们知道Unsafe类是rt.jar包提供的,rt.jar包里面的类是使用Bootstrap类加载器加载的,而我们的启动main函数所在的类是使用AppClassLoader加载的,所以在main函数里面加载Unsafe类时,根据委托机制,会委托给Bootstrap去加载Unsafe类。

如果没有代码8的限制,那么我们的应用程序就可以随意使用Unsafe做事情了,而Unsafe类可以直接操作内存,这是不安全的,所以JDK开发组特意做了这个限制,不让开发人员在正规渠道使用Unsafe类,而是在rt.jar包里面的核心类中使用Unsafe功能。

如果开发人员真的想要实例化Unsafe类,那该如何做?

方法有多种,既然从正规渠道访问不了,那么就玩点黑科技,使用万能的反射来获取Unsafe实例方法。


import sun.misc.Unsafe;import java.lang.reflect.Field;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/11/28 20:05* @mark: show me the code , change the world*/
public class TestUnSafe {// 1 获取 Unsafe的实例static final Unsafe unsafe  ;// 2 记录变量 state在类 Testunsafe中的偏移值static final long stateoffset;// 3 变量private volatile long state = 0;public long getState() {return state;}static {try {// 使用反射获取Unsafe成员变量theUnsafeField field =  Unsafe.class.getDeclaredField("theUnsafe");// 设置为可存取field.setAccessible(true);// 获取该变量的值unsafe  = (Unsafe)field.get(null);// 获取state在TestUnSfate中的偏移量stateoffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state"));} catch (Exception e) {System.out.println(e.getLocalizedMessage());throw new Error(e);}}public static void main(String[] args) {// 5. 创建实例TestUnSafe testUnSafe = new TestUnSafe();System.out.println("修改前:" + testUnSafe.getState());// 6 设置state值为1 通过cas算法boolean b = unsafe.compareAndSwapLong(testUnSafe, stateoffset, 0, 1);System.out.println("修改  " + b);System.out.println("修改前:" + testUnSafe.getState());}}

在如上代码中,通过反射获取unsafe的实例, 运行后输出结果如下。


Java Review - 并发编程_Unsafe相关推荐

  1. Java Review - 并发编程_ 回环屏障CyclicBarrier原理源码剖析

    文章目录 Pre 小Demo 类图结构 CyclicBarrier核心方法源码解读 int await() int await(long timeout, TimeUnit unit) int dow ...

  2. Java Review - 并发编程_ScheduledThreadPoolExecutor原理源码剖析

    文章目录 概述 类结构 核心方法&源码解析 schedule(Runnable command, long delay,TimeUnit unit) scheduleWithFixedDela ...

  3. Java Review - 并发编程_ArrayBlockingQueue原理源码剖析

    文章目录 概述 类图结构 构造函数 主要方法源码解析 offer操作 put操作 poll操作 take操作 peek操作 size 小结 概述 Java Review - 并发编程_LinkedBl ...

  4. Java Review - 并发编程_LinkedBlockingQueue原理源码剖析

    文章目录 概述 类图结构 主要方法 offer操作 概述 Java Review - 并发编程_ConcurrentLinkedQueue原理&源码剖析 介绍了使用CAS算法实现的非阻塞队列C ...

  5. Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析

    文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...

  6. Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析

    文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...

  7. Java Review - 并发编程_前置知识二

    文章目录 What's 多线程并发编程 线程安全问题 共享变量的内存可见性问题 synchronized synchronized的内存语义 volatile - 解决内存可见性 一般在什么时候才使用 ...

  8. Java Review - 并发编程_DelayQueue原理源码剖析

    文章目录 概述 类图结构 小Demo 核心方法&源码解读 offer操作 take操作 poll操作 size操作 小结 概述 DelayQueue并发队列是一个无界阻塞延迟队列,队列中的每个 ...

  9. Java Review - 并发编程_并发List_CopyOnWriteArrayList源码剖析

    文章目录 概述 源码解析 初始化 添加元素 获取指定位置元素 修改指定元素 删除元素 弱一致性的迭代器 CopyOnWriteArrayList 是如何实现弱一致性的 小结 概述 并发包中的并发Lis ...

最新文章

  1. python linux编程与window编程_Python实现Windows和Linux之间互相传输文件(文件夹)的方法...
  2. 对esp和ebp分析来了解函数的调用过程
  3. 【算法分析与设计】实验 分治算法解决中位数问题
  4. python使得ffmpeg更加强大
  5. MySQL数据库、数据表和字段字符集查询、修改和配置
  6. hdu 4794 FIb求循环节
  7. 鸟哥的Linux私房菜服务器架设篇 第三版
  8. 电脑主机服务器中毒文件怎么恢复出厂设置,服务器中毒了 物理文件怎么拷贝呢 以及如何恢复数据呢...
  9. 用计算机编纂家谱的作用,家谱软件的比较
  10. GlassFish 理解
  11. 2019 年中国搜索引擎市场份额排行榜
  12. 如何从华为云服务器上找回手机,华为云服务怎么样使用手机找回功能
  13. 微信公众号【黄小斜】和【Java技术江湖】
  14. 论文流程图——使用VISIO制作论文中的流程图[进阶版]
  15. Unity打包PC包时,在WIN7环境下播放视频崩溃问题的解决记录
  16. 残差网络ResNet最全分析
  17. 国产操作系统--奇思妙想
  18. ESP32 web WiFi 管理器esp32-wifi-manager
  19. matlab定义stem,二维杆状图函数stem
  20. python股票趋势线_如何计算股票图表的趋势线

热门文章

  1. Linux:网络编程
  2. php aapt apk 包名,aapt 命令可应用于查看apk包名、主activity、版本等很多信息
  3. Pycharm 配置 Anaconda中解释器
  4. 深入浅出C++虚函数的vptr与vtable
  5. 稳定匹配算法python实现
  6. python中回文设计_Python中的回文递归
  7. A Self-Attention Setentence Embedding 阅读笔记
  8. 注意力机制~Attention Mechanism
  9. pytorch学习:xavier分布和kaiming分布
  10. 从C语言的角度重构数据结构系列(一)-数据结构入门之逻辑结构与物理结构