96

スレッドに関する上位レベルのJavaクラスの私の先生は、私が確信が持てないことを言いました。

彼は、次のコードは必ずしもready変数を更新するとは限らないと述べました。彼によると、2つのスレッドは必ずしも静的変数を共有するわけではありません。特に、各スレッド(メインスレッドとReaderThread)が独自のプロセッサで実行されているため、同じレジスタ/キャッシュなどと1つのCPUを共有しない場合はそうです。もう一方は更新されません。

ready基本的に、メインスレッドでは更新される可能性がありますが、では更新されない可能性があるReaderThreadため、ReaderThread無限にループする可能性があると彼は言いました。

彼はまた、プログラムが印刷することも可能であると主張し042。印刷方法はわかります42が、わかりません0number彼は、これは変数がデフォルト値に設定されている場合に当てはまると述べました。

静的変数がスレッド間で更新されることはおそらく保証されていないと思いましたが、これはJavaにとって非常に奇妙なことです。volatileを作成すると、readyこの問題は修正されますか?

彼はこのコードを示しました:

public class NoVisibility {  
    private static boolean ready;  
    private static int number;  
    private static class ReaderThread extends Thread {   
        public void run() {  
            while (!ready)   Thread.yield();  
            System.out.println(number);  
        }  
    }  
    public static void main(String[] args) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}
4

7 に答える 7

77

可視性に関しては、静的変数について特別なことは何もありません。それらがアクセス可能である場合、どのスレッドもそれらに到達できるため、それらがより公開されるため、同時実行の問題が発生する可能性が高くなります。

JVMのメモリモデルによって課せられる可視性の問題があります。これは、メモリモデルと、書き込みがスレッドに表示される方法について説明している記事です。発生前の関係を確立しない限り、あるスレッドが他のスレッドにタイムリーに表示されるようになる変更を期待することはできません(実際、JVMは、これらの変更をいつでも表示できるようにする義務はありません)。。

そのリンクからの引用は次のとおりです(Jed Wesley-Smithによるコメントで提供されています):

Java言語仕様の第17章では、共有変数の読み取りや書き込みなどのメモリ操作に関する発生前の関係を定義しています。あるスレッドによる書き込みの結果は、読み取り操作の前に、書き込み操作が発生した場合にのみ、別のスレッドによる読み取りに表示されることが保証されます。同期された揮発性の構造、およびThread.start()メソッドとThread.join()メソッドは、発生前の関係を形成できます。特に:

  • スレッド内の各アクションは、プログラムの順序の後半にあるそのスレッド内のすべてのアクションの前に発生します。

  • モニターのロック解除(同期ブロックまたはメソッド終了)が発生します-同じモニターの後続のすべてのロック(同期ブロックまたはメソッド入力)の前に。また、起こる前の関係は推移的であるため、ロックを解除する前のスレッドのすべてのアクションは、モニターするスレッドのロックに続くすべてのアクションの前に発生します。

  • 揮発性フィールドへの書き込みは、同じフィールドの後続のすべての読み取りの前に発生します。揮発性フィールドの書き込みと読み取りには、モニターの出入りと同様のメモリ整合性の効果がありますが、相互排他ロックは必要ありません。

  • スレッドで開始するための呼び出しが発生します-開始されたスレッドでのアクションの前に。

  • スレッド内のすべてのアクションが発生します-他のスレッドがそのスレッドの結合から正常に戻る前に。

于 2011-02-08T15:31:00.110 に答える
37

彼は可視性について話していて、文字通りに解釈されすぎないようにしています。

静的変数は実際にスレッド間で共有されますが、あるスレッドで行われた変更が別のスレッドにすぐに表示されない場合があり、変数のコピーが2つあるように見えます。

この記事は、彼が情報を提示した方法と一致する見解を示しています。

まず、Javaメモリモデルについて少し理解する必要があります。私はそれを簡潔かつうまく説明するために何年にもわたって少し苦労してきました。今日の時点で、私がそれを説明するために考えることができる最良の方法は、あなたがそれをこのように想像する場合です:

  • Javaの各スレッドは、別々のメモリスペースで実行されます(これは明らかに正しくないので、これについては我慢してください)。

  • メッセージパッシングシステムの場合と同様に、これらのスレッド間で通信が行われることを保証するには、特別なメカニズムを使用する必要があります。

  • あるスレッドで発生するメモリ書き込みは「リークスルー」して別のスレッドに表示される可能性がありますが、これは決して保証されたものではありません。明示的な通信がなければ、どの書き込みが他のスレッドに表示されるか、またはそれらが表示される順序さえも保証できません。

..。

スレッドモデル

しかし、繰り返しになりますが、これは、文字通りJVMがどのように機能するかではなく、スレッド化と揮発性について考えるための単なるメンタルモデルです。

于 2011-02-08T15:35:30.247 に答える
12

基本的には真実ですが、実際には問題はもっと複雑です。共有データの可視性は、CPUキャッシュだけでなく、命令のアウトオブオーダー実行によっても影響を受ける可能性があります。

したがって、Javaはメモリモデルを定義します。これは、スレッドが共有データの一貫した状態を確認できる状況を示します。

特定のケースでは、追加することでvolatile可視性が保証されます。

于 2011-02-08T15:33:26.980 に答える
8

もちろん、両方が同じ変数を参照するという意味で「共有」されますが、必ずしも互いの更新を確認する必要はありません。これは、静的な変数だけでなく、すべての変数に当てはまります。

volatileまた、理論的には、変数が宣言されているか、書き込みが明示的に同期されていない限り、別のスレッドによって行われた書き込みは異なる順序で表示される可能性があります。

于 2011-02-08T15:34:30.470 に答える
5

単一のクラスローダー内では、静的フィールドは常に共有されます。データをスレッドに明示的にスコープするには、のような機能を使用する必要がありますThreadLocal

于 2011-02-08T15:27:40.543 に答える
3

静的プリミティブ型の変数を初期化すると、Javaのデフォルトで静的変数に値が割り当てられます

public static int i ;

このように変数を定義すると、デフォルト値のi=0になります。そのため、0になる可能性があります。その後、メインスレッドがブール値をtrueに更新します。readyは静的変数であるため、メインスレッドと他のスレッドは同じメモリアドレスを参照するため、ready変数が変更されます。したがって、セカンダリスレッドはwhileループから抜け出し、値を出力します。numberの初期化された値を出力するときは0です。メインスレッドがnumber変数を更新する前にスレッドプロセスがwhileループを通過した場合。その後、0を出力する可能性があります

于 2018-04-19T14:31:33.153 に答える
-2

@dontocsata先生に戻って、少し学校に通うことができます:)

現実の世界からのいくつかのメモ、そしてあなたが何を見たり言われたりするかに関係なく。以下の言葉は、示されている正確な順序でこの特定のケースに関するものであることに注意してください。

次の2つの変数は、事実上すべての既知のアーキテクチャの下で同じキャッシュラインに存在します。

private static boolean ready;  
private static int number;  

Thread.exit(メインスレッド)はexit、スレッドグループスレッドの削除(および他の多くの問題)により、終了することが保証され、メモリフェンスを引き起こすことが保証されています。(これは同期された呼び出しであり、デーモンスレッドが残っていない場合など、ThreadGroupも終了する必要があるため、同期部分なしで実装する単一の方法はありません)。

開始されたスレッドReaderThreadはデーモンではないため、プロセスを存続させます。したがってready、とnumberは一緒にフラッシュされ(またはコンテキストスイッチが発生した場合は前の番号)、この場合、少なくとも1つを考えることさえできません。以外のものを見るには、本当に奇妙なものが必要になります42。ここでも、両方の静的変数が同じキャッシュラインにあると思います。4バイトの長さのキャッシュラインや、連続領域(キャッシュライン)にそれらを割り当てないJVMを想像することはできません。

于 2011-02-11T00:06:31.573 に答える