前文综合描述了 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 = "

本文详细探讨了Java并发编程中Varhandle的内存可见性,包括普通变量、opaque、release/acquire和volatile的区别。通过jcstress测试工具,展示了不同情况下的并发行为,揭示了它们在内存可见性和指令重排序方面的特性。
最低0.47元/天 解锁文章
638

被折叠的 条评论
为什么被折叠?



