内存一致性 - Java 中发生之前的关系

在阅读有关内存一致性错误的 Java 文档时。我发现与两个行为相关的点,这些行为在关系发生之前:

  • 当一个语句调用时,每个与该语句有发生前关系的语句也与新线程执行的每个语句都有一个发生在之前的关系。导致创建新线程的代码的影响对新线程是可见的。Thread.start()

  • 当一个线程终止并导致另一个线程返回时,则由终止
    的线程执行的所有语句都与成功连接后的所有语句都具有“发生前”
    关系。线程中代码的效果现在对执行联接的线程可见。Thread.join()

我无法理解它们的含义。如果有人用一个简单的例子来解释它,那就太好了。


答案 1

现代CPU并不总是按照更新的顺序将数据写入内存,例如,如果您运行伪代码(为了简单起见,假设变量始终存储在此处的内存中);

a = 1
b = a + 1

...CPU很可能在写入内存之前写入内存。只要您在单个线程中运行内容,这并不是一个真正的问题,因为运行上述代码的线程在进行赋值后将永远不会看到任何一个变量的旧值。ba

多线程是另一回事,你会认为下面的代码会让另一个线程拾取你的繁重计算的价值;

a = heavy_computation()
b = DONE

...另一个线程正在做...

repeat while b != DONE
    nothing

result = a

但问题是,在将结果存储到内存之前,可能会在内存中设置 done 标志,因此在将计算结果写入内存之前,另一个线程可能会获取内存地址 a 的值。

同样的问题 - 如果Thread.startThread.join没有“发生之前”的保证 - 给你一些代码问题,比如;

a = 1
Thread.start newthread
...

newthread:
    do_computation(a)

...因为在线程启动时可能没有值存储到内存中。a

由于您几乎总是希望新线程能够使用在启动之前初始化的数据,因此具有“发生之前”的保证,即在调用 Thread.start 之前已更新的数据保证对新线程可用。同样的事情也适用于新线程写入的数据保证在终止后对连接它的线程可见的情况。Thread.startThread.join

它只是使线程更容易。


答案 2

请考虑以下情况:

static int x = 0;

public static void main(String[] args) {
    x = 1;
    Thread t = new Thread() {
        public void run() {
            int y = x;
        };
    };
    t.start();
}

主线程已更改字段 。Java 内存模型不保证,如果其他线程未与主线程同步,则此更改对其他线程可见。但是线程将看到此更改,因为主线程调用和JLS保证调用使更改在中可见,因此保证被分配。xtt.start()t.start()xt.run()y1

同样的担忧Thread.join();


推荐