易失性保证和无序执行 [英] Volatile guarantees and out-of-order execution

查看:145
本文介绍了易失性保证和无序执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

重要编辑我知道发生两项任务的线程中的发生之前 我的问题是是否可能 em>线程读取b非空,而a仍然为空。所以我知道如果你从与之前调用 setBothNonNull(...)的线程相同的线程中调用 doIt(),那么它就不能抛出NullPointerException。但是如果一个人从另一个线程调用 doIt() 而不是调用 setBothNonNull(...)的那个?

IMPORTANT EDIT I know about the "happens before" in the thread where the two assignments are happening my question is would it be possible for another thread to be reading "b" non-null while "a" is still null. So I know that if you're calling doIt() from the same thread as the one where you previously called setBothNonNull(...) then it cannot throw a NullPointerException. But what if one is calling doIt() from another thread than the one calling setBothNonNull(...) ?

请注意,此问题仅针对 volatile 关键字以及 volatile 保证:关于 synchronized 关键字是(所以请不要回答你必须使用同步,因为我没有任何问题需要解决:我只是想了解 volatile 关于无序执行的保证(或缺乏保证)。

Note that this question is solely about the volatile keyword and the volatile guarantees: it is not about the synchronized keyword (so please don't answer "you must use synchronize" for I don't have any issue to solve: I simply want to understand the volatile guarantees (or lack of guarantees) regarding out-of-order execution).

假设我们有一个对象包含两个 volatile 字符串引用,这些引用由构造函数初始化为null,并且我们只有一种方法可以修改两个String:通过调用 setBoth(...)并且我们之后只能将它们的引用设置为非空引用(只允许构造函数将它们设置为null)。

Say we have an object containing two volatile String references that are initialized to null by the constructor and that we have only one way to modify the two String: by calling setBoth(...) and that we can only set their references afterwards to non-null reference (only the constructor is allowed to set them to null).

例如(这只是一个例子,毫无疑问):

For example (it's just an example, there's no question yet):

public class SO {

    private volatile String a;
    private volatile String b;

    public SO() {
        a = null;
        b = null;
    }

    public void setBothNonNull( @NotNull final String one, @NotNull final String two ) {
        a = one;
        b = two;
    }

    public String getA() {
        return a;
    }

    public String getB() {
        return b;
    }

}

setBothNoNull(。 ..),分配非空参数a的行出现在指定非空参数b的行之前。

In setBothNoNull(...), the line assigning the non-null parameter "a" appears before the line assigning the non-null parameter "b".

然后如果我这样做(再一次,毫无疑问,接下来会出现问题):

Then if I do this (once again, there's no question, the question is coming next):

doIt() {
    if ( so.getB() != null ) {
        System.out.println( so.getA().length );
    }
}

我的理解是正确的,因为按顺序执行我可以得到一个 NullPointerException

Am I correct in my understanding that due to out-of-order execution I can get a NullPointerException?

换句话说:因为我读了一个非空的b,所以不能保证我会读一个非空的a?

In other words: there's no guarantee that because I read a non-null "b" I'll read a non-null "a"?

因为乱序(多)处理器和 volatile 作品b可以在a之前分配吗?

Because due to out-of-order (multi)processor and the way volatile works "b" could be assigned before "a"?

volatile 保证写入后的读取总是会看到最后写入的值,但是这里有一个 - 订单问题对吧? (再一次,问题是故意用来试图理解 volatile 关键字和Java内存模型的语义,而不是解决问题。) p>

volatile guarantees that reads subsequent to a write shall always see the last written value, but here there's an out-of-order "issue" right? (once again, the "issue" is made on purpose to try to understand the semantics of the volatile keyword and the Java Memory Model, not to solve a problem).

推荐答案

不,你永远不会得到NPE。这是因为 volatile 也具有引入先发生关系的记忆效应。换句话说,它将阻止重新排序

No, you will never get a NPE. This is because volatile also has the memory-effect of introducing a happens-before relationship. In other words, it will prevent reordering of

a = one;
b = two;

上述陈述不会被重新订购,所有线程都会观察到价值一个 a 如果 b 已经有值两个

The statements above, will not be re-ordered, and all threads will observe value one for a if b already has value two.

以下是David Holmes解释的一个帖子:

http://markmail.org/message/j7omtqqh6ypwshfv#query:+page:1+mid: 34dnnukruu23ywzy +州:结果<​​/a>

Here is a thread in which David Holmes explains this:
http://markmail.org/message/j7omtqqh6ypwshfv#query:+page:1+mid:34dnnukruu23ywzy+state:results

编辑(对后续行动的回应):
福尔摩斯所说的是,编译器可以理论上如果只有线程A那么重新排序。但是,还有其他线程,它们可以检测到重新排序。这就是不允许编译器进行重新排序的原因。 java内存模型要求编译器专门确保没有线程会检测到这样的重新排序。

EDIT (response to the follow-up): What Holmes is saying is, the compiler could in theory do a reorder if there were only thread A. However, there ARE other threads, and they CAN detect the reordering. That is why the compiler is NOT allowed to do that reordering. The java memory model requires the compiler specifically to make sure that no thread will ever detect such reordering.


但是如果调用doIt会怎么样( )来自
另一个线程,而不是一个调用
setBothNonNull(...)的线程?

But what if one is calling doIt() from another thread than the one calling setBothNonNull(...) ?

不,你会仍然没有NPE。 volatile 语义确实强加了线程间的排序。这意味着,对于所有现有线程,分配一个在分配两个之前发生。

No, you will still NEVER have a NPE. volatile semantics do impose inter-thread ordering. Meaning that, for all existing thread, assignment of one happens before the assignment of two.

这篇关于易失性保证和无序执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆