为什么指令重排序的 volatile Happens-Before 指令失败?
Why the volatile Happens-Before order for Instruction Reordering fails?
我有以下代码来测试 volatile。 bEnd
和 nCount
定义为可变的。
nCount = 0, bEnd = false
Writer 线程将设置
nCount = 100, bEnd = true
Reader 线程读取这些变量并打印它们。基于 Java Happens-before 顺序,在我看来,当 bEnd = true 时,volatile 确保 nCount = 100。但有时程序会打印这个:
main thread done.
thread Reader running ...
thread Writer running ...
SharedData nCount = 0, bEnd = false
thread Writer bEnd = true
thread Reader nCount = 0, bEnd = true
thread Reader nCount = 100, bEnd = true
thread Reader nCount = 100, bEnd = true
thread Reader done.
Reader如何得到"nCount = 0, bEnd = true"???
下面代码运行上windows10,jdk1.8.0_131
public class HappensBeforeWithVolatile {
public static void main(String[] args) {
Thread threadWriter = new Thread(new Writer());
Thread threadReader = new Thread(new Reader());
threadWriter.start();
threadReader.start();
System.out.println("main thread done.");
}
}
class Writer implements Runnable {
@Override
public void run() {
System.out.println("thread Writer running ...");
SharedData.nCount = 100;
// System.out.println("thread Writer nCount = 100");
SharedData.bEnd = true;
System.out.println("thread Writer bEnd = true");
}
}
class Reader implements Runnable {
@Override
public void run() {
System.out.println("thread Reader running ...");
System.out.println("thread Reader nCount = " + SharedData.nCount + ", bEnd = " + SharedData.bEnd);
System.out.println("thread Reader nCount = " + SharedData.nCount + ", bEnd = " + SharedData.bEnd);
if (SharedData.nCount == 0 && SharedData.bEnd) {
System.out.println("thread Reader CODE REORDER !!!");
}
System.out.println("thread Reader nCount = " + SharedData.nCount + ", bEnd = " + SharedData.bEnd);
System.out.println("thread Reader done.");
}
}
class SharedData {
volatile public static boolean bEnd = false;
volatile public static int nCount = 0;
static {
System.out.println("SharedData nCount = " + nCount + ", bEnd = " + bEnd);
}
}
volatile 确保当 bEnd = true 时 nCount = 100
从技术上讲,是的。但是 reader 并没有自动读取它们。所以它可能会打印 nCount = 0 and bEnd = true
.
这是一个例子:
- Reader 读取
nCount 0
- 维特写道
nCount = 100
- 维特写道
bEnd = true
- 作者打印
thread Writer bEnd = true
- Reader 读取
bEnd true
顺便说一句,您的整个示例都有点缺陷。要测试之前发生的情况,您需要测试后续操作,即使 bEnd
易变且 nCount
不是易变的,然后将您的示例简化为:
static class Reader implements Runnable {
@Override
public void run() {
while (true) {
if (SharedData.bEnd) {
System.out.println(SharedData.nCount);
break;
} else {
System.out.println("Not yet seen as true");
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
}
}
}
}
static class Writer implements Runnable {
@Override
public void run() {
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
SharedData.nCount = 100;
SharedData.bEnd = true;
}
}
这将始终输出 100
(至少在这种情况下)。正确的解释是,如果 Reader
线程看到 Write
线程对 volatile 变量的更新,它将看到之前完成的所有操作,因此 100
.
我有以下代码来测试 volatile。 bEnd
和 nCount
定义为可变的。
nCount = 0, bEnd = false
Writer 线程将设置
nCount = 100, bEnd = true
Reader 线程读取这些变量并打印它们。基于 Java Happens-before 顺序,在我看来,当 bEnd = true 时,volatile 确保 nCount = 100。但有时程序会打印这个:
main thread done.
thread Reader running ...
thread Writer running ...
SharedData nCount = 0, bEnd = false
thread Writer bEnd = true
thread Reader nCount = 0, bEnd = true
thread Reader nCount = 100, bEnd = true
thread Reader nCount = 100, bEnd = true
thread Reader done.
Reader如何得到"nCount = 0, bEnd = true"???
下面代码运行上windows10,jdk1.8.0_131
public class HappensBeforeWithVolatile {
public static void main(String[] args) {
Thread threadWriter = new Thread(new Writer());
Thread threadReader = new Thread(new Reader());
threadWriter.start();
threadReader.start();
System.out.println("main thread done.");
}
}
class Writer implements Runnable {
@Override
public void run() {
System.out.println("thread Writer running ...");
SharedData.nCount = 100;
// System.out.println("thread Writer nCount = 100");
SharedData.bEnd = true;
System.out.println("thread Writer bEnd = true");
}
}
class Reader implements Runnable {
@Override
public void run() {
System.out.println("thread Reader running ...");
System.out.println("thread Reader nCount = " + SharedData.nCount + ", bEnd = " + SharedData.bEnd);
System.out.println("thread Reader nCount = " + SharedData.nCount + ", bEnd = " + SharedData.bEnd);
if (SharedData.nCount == 0 && SharedData.bEnd) {
System.out.println("thread Reader CODE REORDER !!!");
}
System.out.println("thread Reader nCount = " + SharedData.nCount + ", bEnd = " + SharedData.bEnd);
System.out.println("thread Reader done.");
}
}
class SharedData {
volatile public static boolean bEnd = false;
volatile public static int nCount = 0;
static {
System.out.println("SharedData nCount = " + nCount + ", bEnd = " + bEnd);
}
}
volatile 确保当 bEnd = true 时 nCount = 100
从技术上讲,是的。但是 reader 并没有自动读取它们。所以它可能会打印 nCount = 0 and bEnd = true
.
这是一个例子:
- Reader 读取
nCount 0
- 维特写道
nCount = 100
- 维特写道
bEnd = true
- 作者打印
thread Writer bEnd = true
- Reader 读取
bEnd true
顺便说一句,您的整个示例都有点缺陷。要测试之前发生的情况,您需要测试后续操作,即使 bEnd
易变且 nCount
不是易变的,然后将您的示例简化为:
static class Reader implements Runnable {
@Override
public void run() {
while (true) {
if (SharedData.bEnd) {
System.out.println(SharedData.nCount);
break;
} else {
System.out.println("Not yet seen as true");
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
}
}
}
}
static class Writer implements Runnable {
@Override
public void run() {
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
SharedData.nCount = 100;
SharedData.bEnd = true;
}
}
这将始终输出 100
(至少在这种情况下)。正确的解释是,如果 Reader
线程看到 Write
线程对 volatile 变量的更新,它将看到之前完成的所有操作,因此 100
.