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 操作等等。
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 并发编程
版权声明: 本文为 InfoQ 作者【码农架构】的原创文章。
原文链接:【http://xie.infoq.cn/article/05516b2a11d576b74e49df634】。文章转载请联系作者。
评论 (2 条评论)