42

重要な編集2つの割り当てが行われているスレッドの「前に起こる」について知っています。私の質問は、 「a」がまだnullである間に、別のスレッドが「b」をnull以外で読み取る可能性があるかどうかです。したがって、以前にsetBothNonNull(...)を呼び出したスレッドと同じスレッドからdoIt()を呼び出している場合、NullPointerExceptionをスローすることはできません。しかし、setBothNonNull(...)を呼び出すスレッドとは別のスレッドからdoIt()を呼び出す場合はどうなるでしょうか。

この質問はvolatileキーワードとvolatile保証に関するものであることに注意してください。キーワードに関するものではありません(したがって、解決する問題がないため、「同期を使用する必要があります」と答えないでください。単に保証synchronizedを理解したいだけです。volatile(または保証の欠如)アウトオブオーダー実行に関して)。

volatileコンストラクターによってnullに初期化される2つの文字列参照を含むオブジェクトがあり、2つの文字列を変更する方法は1つしかないとします。つまり、 setBoth(...)を呼び出し、後でそれらの参照を非に設定することしかできません。 null参照(コンストラクターのみがそれらをnullに設定できます)。

たとえば(これは単なる例であり、まだ質問はありません):

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(...)では、非nullパラメータ「a」を割り当てる行が非nullパラメータ「b」を割り当てる行の前に表示されます。

次に、これを行うと(もう一度、質問はありません。質問は次に来ます):

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

アウトオブオーダー実行のためにNullPointerExceptionが発生する可能性があることを理解していますか?

言い換えると、null以外の「b」を読み取ったためにnull以外の「a」を読み取るという保証はありませんか?

アウトオブオーダー(マルチ)プロセッサとvolatile動作方法により、「a」の前に「b」を割り当てることができますか?

volatile書き込みに続く読み取りは常に最後に書き込まれた値を参照することを保証しますが、ここには順序が正しくない「問題」がありますか?volatile(繰り返しになりますが、「問題」は、問題を解決するためではなく、キーワードとJavaメモリモデルのセマンティクスを理解しようとすることを目的としています)。

4

5 に答える 5

26

いいえ、NPEを取得することはありません。これはvolatile、起こる前の関係を導入するというメモリー効果もあるためです。言い換えれば、それは再注文を防ぎます

a = one;
b = two;

上記のステートメントは並べ替えられず、すべてのスレッドがすでに値を持っている場合は値oneを監視します。abtwo

これは、David Holmesがこれを説明しているスレッドです:http://markmail.org/message/j7omtqqh6ypwshfv#query:+ page:1 + mid:
34dnnukruu23ywzy + state :results

編集(フォローアップへの応答):ホームズが言っていることは、理論的には、スレッドAしかない場合、コンパイラーは並べ替えを実行できます。ただし、他のスレッドがあり、並べ替えを検出できます。そのため、コンパイラはその並べ替えを行うことができません。Javaメモリモデルでは、スレッドがそのような並べ替えを検出しないようにするために、コンパイラが特に必要です。

しかし、setBothNonNull(...)を呼び出すスレッドとは別のスレッドからdoIt()を呼び出す場合はどうなるでしょうか。

いいえ、まだNPEはありません。volatileセマンティクスはスレッド間の順序付けを課します。つまり、既存のすべてのスレッドでは、の割り当てはoneの割り当ての前に行われtwoます。

于 2010-03-14T06:39:33.610 に答える
8

アウトオブオーダー実行のためにNullPointerExceptionが発生する可能性があることを理解していますか?言い換えると、null以外の「b」を読み取ったためにnull以外の「a」を読み取るという保証はありませんか?

null以外に割り当てられた値を想定すると、aあなたの理解は正しくないbと思います。JLSはこれを言います:

1)xとyが同じスレッドのアクションであり、プログラムの順序でxがyの前にある場合、hb(x、y)。

2)アクションxが次のアクションyと同期する場合、hb(x、y)もあります。

3)hb(x、y)およびhb(y、z)の場合、hb(x、z)。

4)揮発性変数(§8.3.1.4)への書き込みは同期します-任意のスレッドによるv後続のすべての読み取りと同期します(後続は同期順序に従って定義されます)。v

定理

スレッド#1が1setBoth(...);回呼び出され、引数がnullでなく、スレッド#2がbnullでないことが確認された場合、スレッド#2はnullを確認できませんa

非公式の証明

  1. By(1)-スレッド#1のhb(write(a、non-null)、write(b、non-null))
  2. 2)と(4)によって-hb(write(b、non-null)、read(b、non-null))
  3. スレッド#2の(1)-hb(read(b、non-null)、read(a、XXX))によって、
  4. 4)-hb(write(a、non-null)、read(b、non-null))
  5. 4)-hb(write(a、non-null)、read(a、XXX))

言い換えると、aの値(XXX)の読み取りの「発生前」へのnull以外の値の書き込みa。XXXがnullになる唯一の方法はa、hb(write(a、non-null)、write(a、XXX))およびhb(write(a、XXX)、read (a、XXX))。そして、これは問題の定義によれば不可能であり、したがってXXXをnullにすることはできません。QED。

説明-JLSは、hb(...)( "happens-before")関係が並べ替えを完全に禁止しているわけではないと述べています。ただし、hb(xx、yy)の場合、アクションxxおよびyyの並べ替えは、結果のコードが元のシーケンスと同じ観察可能な効果を持つ場合にのみ許可されます。

于 2010-03-14T06:00:03.563 に答える
2

volatileは、この場合に同期されたものと同じ順序付けセマンティクスを持っていることを説明する次の投稿を見つけました。JavaVolatileは強力です

于 2010-03-14T07:16:34.373 に答える
2

Stephen Cと受け入れられた回答は適切であり、ほとんどカバーしていますが、変数aは揮発性である必要はなく、NPEを取得できないことに注意してください。これは、であるかどうかに関係なく、a = oneとの間に起こる前の関係があるためです。したがって、Stephen Cの正式な証明は引き続き適用され、揮発性である必要はありません。b = twoavolatilea

于 2013-03-04T14:21:52.140 に答える
0

このページを読んで、あなたの質問の不揮発性で同期されていないバージョンを見つけました:

class Simple {
    int a = 1, b = 2;
    void to() {
        a = 3;
        b = 4;
    }
    void fro() {
        System.out.println("a= " + a + ", b=" + b);
    }
}

froは、の値に対して1または3のいずれかをa取得でき、独立して、の値に対して2または4のいずれかを取得できますb

(これはあなたの質問に答えないことを理解していますが、それを補完します。)

于 2010-03-14T06:05:32.560 に答える