6

私は、2 つのスレッド間で共有される変数はスレッドローカルにキャッシュでき、揮発性として宣言する必要があると信じていました。しかし、その信念は最近、チームメイトによって異議を唱えられました。次の場合に volatile が必要かどうかを調べています。

class Class1
{
   void Method1()
   {
      Worker worker = new Worker();
      worker.start();
      ...
      System.out.println(worker.value); // want to poll value at this instant
      ...
   }

   class Worker extends Thread
   {
      int value = 0; // Should this be declared as a volatile?
      public void run()
      {
         ...
         value = 1; // this is the only piece of code that updates value
         ...
       }
   }
}

ここで私の主張は、Worker (子) スレッドがスレッド内の Worker オブジェクトの変数「値」をキャッシュし、値を 1 に設定しながらそのコピーだけを更新した可能性があるということです。そのような場合、メイン スレッドは、更新された値が表示されない場合があります。

しかし、私のチームメイトは、「値」へのアクセスはオブジェクト (ワーカー) を介して行われているため、両方のスレッドが異なる値を参照できるため、両方のスレッドが「ワーカー」オブジェクト自体の個別のコピーを保持している場合にのみ可能であると考えています。 (これはさらに、スレッドの作成には、すべての共有オブジェクトのディープ コピーの作成が含まれることを意味します)。

すべての共有オブジェクトの完全に異なるコピーを各スレッドが保持するのは非常に非効率的であるため、これは真実ではないことがわかりました。したがって、私は深刻な疑問を抱いています。メインスレッドで「worker.value」を実行すると、子スレッドで「this.value」を実行する場合とは異なるメモリ位置が参照されますか? 子 (ワーカー) スレッドは「値」をキャッシュしますか?

よろしく。

4

2 に答える 2

7

ここで私の主張は、Worker (子) スレッドが Worker オブジェクトの変数「値」をスレッドローカルにキャッシュし、値を 1 に設定しながらそのコピーだけを更新した可能性があるということです。そのような場合、メイン スレッドは、更新された値が表示されない場合があります。

あなたは正しいです。両方とも同じインスタンスを扱っていても、のフィールドWorkerのキャッシュされたメモリ バージョンがWorker、さまざまな異なるスレッド メモリ キャッシュ間で同期されているという保証はありません。

フィールドは、他のスレッドがフィールドへの更新を確実に認識valueできるようにマークする必要があります。volatilevalue = 1;value

しかし、私のチームメイトは、「値」へのアクセスはオブジェクト (ワーカー) を介して行われているため、両方のスレッドが異なる値を参照できるため、両方のスレッドが「ワーカー」オブジェクト自体の個別のコピーを保持している場合にのみ可能であると考えています。 ...

いいえ、これは正しくありません。スレッド メモリに関する注意が必要な部分は、プロセッサ メモリ キャッシュに関係しています。によって課せられるメモリ バリアがなければ、volatileプロセスは完全に自由にメモリをキャッシュできます。したがって、両方のスレッドが の同じインスタンスで動作していても、 に関連付けられたメモリのコピーWorkerがローカルにキャッシュされている可能性があります。Worker

スレッド アーキテクチャは、常に中央ストレージを参照するのではなく、個別の高速プロセッサ ローカル メモリで動作するため、速度が大幅に向上します。

于 2012-06-15T16:01:17.160 に答える
2

しかし、私のチームメイトは、「値」へのアクセスはオブジェクト (ワーカー) を介して行われているため、両方のスレッドが異なる値を参照できるため、両方のスレッドが「ワーカー」オブジェクト自体の個別のコピーを保持している場合にのみ可能であると考えています。 (これはさらに、スレッドの作成には、すべての共有オブジェクトのディープ コピーの作成が含まれることを意味します)。

あなたの同僚が気付いていないのは、インスタンス変数の値 (その問題については任意の変数) が一時的にマシンのレジスター、またはプロセッサーの第 1 レベルまたは第 2 レベルのメモリーキャッシュにキャッシュされる可能性があるということです。Java 言語仕様では、適切な手順を実行しない限り、2 つのスレッドが同じ変数に対して必ずしも同じ値を参照するとは限らないと明示的に述べています。

この問題を扱う JLS のセクション全体があります: JLS 17.4。Java がこの分野でどのように動作するかについて議論する場合は、あなたと同僚の両方がこれと 17.5 および 17.6 を読むことをお勧めします。または、JLS よりも読みやすい Brian Goetz らによる「Java Concurrency in Practice」の最後の章を読むこともできます。

あなたとあなたの同僚は、スレッド化が機能するはずだという直感に頼らないことをお勧めします。仕様を読んでください。スレッドの振る舞いのいくつかの側面は直観的ではありません

于 2012-06-15T16:21:23.873 に答える