写点什么

Java 并发编程:AQS 的原子性如何保证

用户头像
码农架构
关注
发布于: 2020 年 12 月 21 日
Java并发编程:AQS的原子性如何保证

当我们研究 AQS 框架时(对于 AQS 不太熟知可以先阅读《什么是JDK内置并发框架AQS》,会发现 AbstractQueuedSynchronizer 这个类很多地方都使用了 CAS 操作。在并发实现中 CAS 操作必须具备原子性,而且是硬件级别的原子性。我们知道 Java 被隔离在硬件之上,硬件级别的操作明显力不从心。这时为了能够执行操作系统层面的操作,就必须要通过用 C++编写的 native 本地方法来扩展实现。一般可以通过 JNI 方式实现 Java 代码调用 C++代码


Unsafe 调用

JDK 提供了一个类来满足 CAS 的硬件级别原子性要求,即 sun.misc.Unsafe 类,从名字上大概知道它用于执行低级别、不安全的操作,AQS 就是使用此类来完成硬件级别的原子操作。也就是说通过该类就能实现对处理器的原子操作,Unsafe 通过 JNI 调用本地 C++代码,C++代码调用了硬件指令集,这些硬件指令集都属于 CPU。


Unsafe 的魔法

Unsafe 是一个很强大的类,它可以分配内存、释放内存、可以定位对象某字段的位置、可以修改对象的字段值、可以使线程挂起、使线程恢复、可进行硬件级别原子的 CAS 操作等等。

image.png

Unsafe 的用途

一般情况下,我们都没有特殊的需求去使用它。而且它的使用必须限定在受信任代码,这些代码一般由 JVM 指定。例如直接 Unsafe unsafe = Unsafe.getUnsafe();获取一个 Unsafe 实例是不会成功的,因为这个类的安全性很重要,设计者对其进行了安全检查。如下代码所示,它会检测调用它的类是否由启动类加载器 Bootstrap ClassLoader(它的类加载器为 null)加载,由此来保证此类只能由 JVM 指定的类使用。


Unsafe 实现 CAS

因为存在安全性问题,所以如果我们要用 Unsafe 类则需要另辟蹊径。可行的方法就是通过反射来绕过上述的安全检查,我们可以通过以下的 getUnsafeInstance 方法来获取 Unsafe 实例。这段代码演示了如何获取 Java 对象的相对地址偏移量以及使用 Unsafe 来完成 CAS 操作,最终输出的是 flag 字段的内存偏移量及 CAS 操作后的值。最终的输出为“unsafeTest 对象的 flag 字段的地址偏移量为:12”和“CAS 操作后的 flag 值为:101”。另外如果使用开发工具如 Eclipse,可能会编译通不过,只要把编译错误提示关掉即可。


总结

这里主要讲解了 Unsafe 类如何让 Java 层能实现硬件级别的原子操作,同时也了解了 Unsafe 类拥有很多法魔技能。通常我们使用 Java 时不需要在内存中处理 Java 对象及内存地址位置,但有的时候我们被迫必须要操作 Java 对象相关的地址,于是我们只能使用 Unsafe 类。使用该类则意味着破坏了 Java 平台隔离的效果了,我们都知道一旦用了本地方法则可能会引来跨平台问题。

Java 并发编程



发布于: 2020 年 12 月 21 日阅读数: 1654
用户头像

码农架构

关注

公众号:码农架构 2018.03.22 加入

专注于系统架构、高可用、高性能、高并发类技术分享

评论 (2 条评论)

发布
用户头像
unsafe的用途和unsafe实现cas的描述是一样的。内容重复
2020 年 12 月 23 日 08:24
回复
已经修改!技术号的可能改不了,字数有点多~
2020 年 12 月 23 日 11:08
回复
没有更多了
Java并发编程:AQS的原子性如何保证