为什么易失性“发生在指令重新排序之前”发生故障?

时间:2018-08-08 08:44:47

标签: java multithreading volatile

我拥有以下代码来测试 volatile bEndnCount被定义为易失性。

nCount = 0, bEnd = false

Writer线程将设置

nCount = 100, bEnd = true

Reader线程读取这些可变对象并进行打印。我认为,基于Java先发生顺序, volatile确保当bEnd = true时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.

阅读器如何获取“ 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);
    }
}

2 个答案:

答案 0 :(得分:3)

volatile确保bEnd = true时nCount = 100

从技术上讲,是的。但是读者并没有原子地阅读它们。因此它可能会打印<?xml version="1.0" encoding="UTF-8"?><process version="9.0.000-BETA"> <context> <input/> <output/> <macros/> </context> <operator activated="true" class="process" compatibility="9.0.000-BETA" expanded="true" name="Process"> <process expanded="true"> <operator activated="true" class="subprocess" compatibility="9.0.000-BETA" expanded="true" height="82" name="Subprocess" width="90" x="112" y="34"> <process expanded="true"> <operator activated="true" class="retrieve" compatibility="9.0.000-BETA" expanded="true" height="68" name="Retrieve Iris" width="90" x="45" y="34"> <parameter key="repository_entry" value="//Samples/data/Iris"/> </operator> <operator activated="true" class="declare_missing_value" compatibility="9.0.000-BETA" expanded="true" height="82" name="Declare Missing Value" width="90" x="179" y="34"> <parameter key="attribute_filter_type" value="single"/> <parameter key="attribute" value="a1"/> <parameter key="mode" value="expression"/> <parameter key="expression_value" value="a1 &lt;5"/> </operator> <operator activated="true" class="select_attributes" compatibility="9.0.000-BETA" expanded="true" height="82" name="Select Attributes" width="90" x="380" y="34"> <parameter key="attribute_filter_type" value="subset"/> <parameter key="attributes" value="a2|a1"/> </operator> <connect from_op="Retrieve Iris" from_port="output" to_op="Declare Missing Value" to_port="example set input"/> <connect from_op="Declare Missing Value" from_port="example set output" to_op="Select Attributes" to_port="example set input"/> <connect from_op="Select Attributes" from_port="example set output" to_port="out 1"/> <portSpacing port="source_in 1" spacing="0"/> <portSpacing port="sink_out 1" spacing="0"/> <portSpacing port="sink_out 2" spacing="0"/> </process> </operator> <operator activated="true" class="generate_attributes" compatibility="9.0.000-BETA" expanded="true" height="82" name="Generate Attributes" width="90" x="447" y="34"> <list key="function_descriptions"> <parameter key="a1_new" value="if(a1==MISSING_NUMERIC, a2,a1)"/> </list> </operator> <connect from_op="Subprocess" from_port="out 1" to_op="Generate Attributes" to_port="example set input"/> <connect from_op="Generate Attributes" from_port="example set output" to_port="result 1"/> <portSpacing port="source_input 1" spacing="0"/> <portSpacing port="sink_result 1" spacing="0"/> <portSpacing port="sink_result 2" spacing="0"/> <description align="center" color="yellow" colored="false" height="181" resized="true" width="529" x="275" y="126">With the expression parser more complex statements can be defined. In this case:&lt;br&gt;if(a1==MISSING_NUMERIC, a2,a1)&lt;br/&gt;&lt;br/&gt;meaning that if the value of attribute a1 is missing, it will be replaced by the value of a2 otherwise the value of a1 is kept.&lt;br/&gt;&lt;br/&gt;Instead of creating a new attribute the old one can also be overwritten&lt;br/&gt;&lt;br&gt;</description> </process> </operator> </process>

这里是一个例子:

  1. 阅读器读取nCount = 0 and bEnd = true
  2. Wirter写nCount 0
  3. Wirter写nCount = 100
  4. 作家打印bEnd = true
  5. 阅读器读取thread Writer bEnd = true

答案 1 :(得分:1)

您的整个示例有点瑕疵。要测试之前发生的情况,您需要测试后续操作,即使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线程看到volatile变量的Write线程的更新,它将看到在此之前所做的所有事情,因此100