Java Review - 并发编程_Unsafe
文章目录
- 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相关推荐
- Java Review - 并发编程_ 回环屏障CyclicBarrier原理源码剖析
文章目录 Pre 小Demo 类图结构 CyclicBarrier核心方法源码解读 int await() int await(long timeout, TimeUnit unit) int dow ...
- Java Review - 并发编程_ScheduledThreadPoolExecutor原理源码剖析
文章目录 概述 类结构 核心方法&源码解析 schedule(Runnable command, long delay,TimeUnit unit) scheduleWithFixedDela ...
- Java Review - 并发编程_ArrayBlockingQueue原理源码剖析
文章目录 概述 类图结构 构造函数 主要方法源码解析 offer操作 put操作 poll操作 take操作 peek操作 size 小结 概述 Java Review - 并发编程_LinkedBl ...
- Java Review - 并发编程_LinkedBlockingQueue原理源码剖析
文章目录 概述 类图结构 主要方法 offer操作 概述 Java Review - 并发编程_ConcurrentLinkedQueue原理&源码剖析 介绍了使用CAS算法实现的非阻塞队列C ...
- Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析
文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...
- Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析
文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...
- Java Review - 并发编程_前置知识二
文章目录 What's 多线程并发编程 线程安全问题 共享变量的内存可见性问题 synchronized synchronized的内存语义 volatile - 解决内存可见性 一般在什么时候才使用 ...
- Java Review - 并发编程_DelayQueue原理源码剖析
文章目录 概述 类图结构 小Demo 核心方法&源码解读 offer操作 take操作 poll操作 size操作 小结 概述 DelayQueue并发队列是一个无界阻塞延迟队列,队列中的每个 ...
- Java Review - 并发编程_并发List_CopyOnWriteArrayList源码剖析
文章目录 概述 源码解析 初始化 添加元素 获取指定位置元素 修改指定元素 删除元素 弱一致性的迭代器 CopyOnWriteArrayList 是如何实现弱一致性的 小结 概述 并发包中的并发Lis ...
最新文章
- python linux编程与window编程_Python实现Windows和Linux之间互相传输文件(文件夹)的方法...
- 对esp和ebp分析来了解函数的调用过程
- 【算法分析与设计】实验 分治算法解决中位数问题
- python使得ffmpeg更加强大
- MySQL数据库、数据表和字段字符集查询、修改和配置
- hdu 4794 FIb求循环节
- 鸟哥的Linux私房菜服务器架设篇 第三版
- 电脑主机服务器中毒文件怎么恢复出厂设置,服务器中毒了 物理文件怎么拷贝呢 以及如何恢复数据呢...
- 用计算机编纂家谱的作用,家谱软件的比较
- GlassFish 理解
- 2019 年中国搜索引擎市场份额排行榜
- 如何从华为云服务器上找回手机,华为云服务怎么样使用手机找回功能
- 微信公众号【黄小斜】和【Java技术江湖】
- 论文流程图——使用VISIO制作论文中的流程图[进阶版]
- Unity打包PC包时,在WIN7环境下播放视频崩溃问题的解决记录
- 残差网络ResNet最全分析
- 国产操作系统--奇思妙想
- ESP32 web WiFi 管理器esp32-wifi-manager
- matlab定义stem,二维杆状图函数stem
- python股票趋势线_如何计算股票图表的趋势线
热门文章
- Linux:网络编程
- php aapt apk 包名,aapt 命令可应用于查看apk包名、主activity、版本等很多信息
- Pycharm 配置 Anaconda中解释器
- 深入浅出C++虚函数的vptr与vtable
- 稳定匹配算法python实现
- python中回文设计_Python中的回文递归
- A Self-Attention Setentence Embedding 阅读笔记
- 注意力机制~Attention Mechanism
- pytorch学习:xavier分布和kaiming分布
- 从C语言的角度重构数据结构系列(一)-数据结构入门之逻辑结构与物理结构