前文综合描述了 Varhandle
以及 Varhandle
能够做的事情,但是要了解并使用 Varhandle
并非是一件容易的事。总的来说,要想很好地使用 Varhandle
,必须先了解plain(普通方式)、opaque、release/acquire、volatile 的区别及使用。
结合前面所学习的 jcstress ,本文用 jcsstress 作为并发测试工具来结合一些例子说明 plain、opaque、release/acqiure、volatile的特性。
如果不知道 jcstress 的使用的话,可以参考下 JUC整理笔记三之测试工具jcstress
内存可见性的区别
普通变量
@JCStressTest(Mode.Termination)
@Outcome(id = "STALE", expect = Expect.ACCEPTABLE)
@Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE)
public static class PlainTester {
private int x = 0;
@Actor
public void actor() {
while (x == 0) {
//do nothing
}
}
@Signal
public void signal() {
x = 1;
}
}
上面测试案例是有一个 while 循环,然后另外一个线程修改 x 的值来达到让循环终止的目的,其测试结果 STALE、TERMINATED 并行存在, 说明在测试案例中,是有存在修改 x 值后,循环是没有结束的案例。
结果表明,多线程的环境中,普通变量是无法保证线程内存可见性的 。
opaque
@JCStressTest(Mode.Termination)
@Outcome(id = "STALE", expect = Expect.FORBIDDEN)
@Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE)
public static class OpaqueTester {
private volatile int x = 0;
private static final VarHandle X; d
static {
try {
X = MethodHandles.lookup().findVarHandle(OpaqueTester.class, "x", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
@Actor
public void actor() {
while ((int) X.getOpaque(this) == 0) {
//do nothing
}
}
@Signal
public void signal() {
X.setOpaque(this, 1);
}
}
这里使用了 Varhanle 来测试 opaque 的功能,测试的 Outcome 是不存在 STALE 。
结果表明,多线程环境中, opaque 是可以保存内存可见性的 。
release/acquire
@JCStressTest(Mode.Termination)
@Outcome(id = "STALE", expect = Expect.FORBIDDEN)
@Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE)
public static class ReleaseAcquireTester {
private volatile int x = 0;
private static final VarHandle X;
static {
try {
X = MethodHandles.lookup().findVarHandle(ReleaseAcquireTester.class, "x", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
@Actor
public void actor() {
while ((int) X.getAcquire(this) == 0) {
//do nothing
}
}
@Signal
public void signal() {
X.setRelease(this, 1);
}
}
该例子和 opaque 一样,也是不存在 STALE
结果表明,多线程环境下,release/acquire 是可以保证内存可见性的 。
volatile
@JCStressTest(Mode.Termination)
@Outcome(id = "