Java语言和JVM平台已经度过了20岁的生日。它最初起源于机顶盒、移动设备和Java-Card,同时也应用在了各种服务器系统中,Java已成为物联网(Internet of Things)的通用语言。我们显然可以看到Java已经无处不在!\
但是不那么为人所知的是,Java也广泛应用于各种低延迟的应用中,如游戏服务器和高频率的交易应用。这只所以能够实现要归功于Java的类和包在可见性规则中有一个恰到好处的漏洞,让我们能够使用一个很便利的类,这个类就是sun.misc.Unsafe。这个类从过去到现在一直都有着很大的分歧,有些人喜欢它,而有些人则强烈地讨厌它——但关键的一点在于,它帮助JVM和Java生态系统演化成了今天的样子。基本上可以说,Unsafe类为了速度,在Java严格的安全标准方面做了一些妥协。\
如果在Java世界中移除了sun.misc.Unsafe(和一些较小的私有API),并且没有足够的API来替代的话,那Java世界将会发生什么呢,针对这一点引发了热烈的讨论,包括在JCrete上、“sun.misc.Unsafe会发生什么”论文以及在DripStat像这样的博客文章。Oracle的最终提议(JEP260)解决了这个问题,它提供了一个很好的迁移路径。但问题依然存在——在Unsafe真的消失后,Java世界将会是什么样子呢?\
组织
\乍看上去,sun.misc.Unsafe的特性集合可能会让我们觉得有些混乱,它一站式地提供了各种特性。\
我试图将这些特性进行分类,可以得到如下5种使用场景:\
- 对变量和数组内容的原子访问,自定义内存屏障 \
- 对序列化的支持 \
- 自定义内存管理/高效的内存布局 \
- 与原生代码和其他JVM进行互操作 \
- 对高级锁的支持
在我们试图为这些功能寻找替代实现时,至少在最后一点上可以宣告胜利。Java早就有了强大(坦白说也很漂亮)的官方API,这就是java.util.concurrent.LockSupport。\
原子访问
\原子访问是sun.misc.Unsafe被广泛应用的特性之一,特性包括简单的“put”和“get”操作(带有volatile语义或不带有volatile语义)以及比较并交换(compare and swap,CAS)操作。
public long update() {\ for(;;) {\ long version = this.version;\ long newVersion = version + 1;\ if (UNSAFE.compareAndSwapLong(this, VERSION_OFFSET, version, newVersion)) {\ return newVersion;\ }\ }\}
\
但是,请稍等,Java不是已经通过官方API为该功能提供了支持吗?绝对是这样的,借助Atomic类确实能够做到,但是它会像基于sun.misc.Unsafe的API一样丑陋,在某些方面甚至更糟糕,让我们看一下到底为什么。\
AtomicX类实际上是真正的对象。假设我们要维护一个存储系统中的某条记录,并且希望能够跟踪一些特定的统计数据或元数据,比如版本的计数:
public class Record {\ private final AtomicLong version = new AtomicLong(0);\\ public long update() {\ return version.incrementAndGet();\ }\}
\
尽管这段代码非常易读,但是它却污染到了我们的堆,因为每条数据记录都对应两个不同的对象,而不是一个对象,具体来讲,这两个对象也就是Atomic实例以及实际的记录本身。它所导致的问题不仅仅是产生无关的垃圾,而且会导致额外的内存占用以及Atomic实例的解引用(dereference)操作。\
但是,我们可以做的更好一点——还有另外一个API,那就是java.util.concurrent.atomic.AtomicXFieldUpdater类。\
AtomixXFieldUpdater是正常Atomic类的内存优化版本,它牺牲了API的简洁性来换取内存占用的优化。通过该组件的单个实例就能支持某个类的多个实例,在我们的Record场景中,可以用它来更新volatile域。
public class Record {\ private static final AtomicLongFieldUpdater\u0026lt;Record\u0026gt; VERSION =\ AtomicLongFieldUpdater.newUpdater(Record.class, \"version\");\\ private volatile long version = 0;\\ public long update() {\ return VERSION.incrementAndGet(this);\ }\}
\
在对象创建方面,这种方式能够生成更为高效的代码。同时,这个updater是一个静态的final域,对于任意数量的record,只需要有一个updater就可以了,并且最重要的是,它现在就是可用的。除此之外,它还是一个受支持的公开API,它始终应该是优选的策略。不过,另一方面,我们看一下updater的创建和使用方式,它依然非常丑陋,不是非常易读,坦白说,凭直觉看不出来它是个计数器。\
那么,我们能更好一点吗?是的,变量句柄(Variable Handles)(或者简洁地称之为“VarHandles”)目前正处于设计阶段,它提供了一种更有吸引力的API。\
VarHandles是对数据行为(data-behavior)的一种抽象。它们提供了类似volatile的访问方式,不仅能够用在域上,还能用于数组或buffers中的元素上。\
乍看上去,下面的样例可能显得有些诡异,所以我们看一下它是如何实现的。
public class Record {\ private static final VarHandle VERSION;\\ static {\ try {\ VERSION = MethodHandles.lookup().findFieldVarHandle\ (Record.class, \"version\