3

私はhereの例に従っています

processCommandas-を修正しました。

private void processCommand() throws InterruptedException {
        this.command = "xyz";
}

完全なコード-

import java.util.logging.Level;
import java.util.logging.Logger;

public class WorkerThread implements Runnable {

    private String command;

    public WorkerThread(String s) {
        this.command = s;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);
        }

        System.out.println(Thread.currentThread().getName() + " Commad at start :" + command);
        try {
            processCommand();
        } catch (InterruptedException ex) {
        }
        System.out.println(Thread.currentThread().getName() + " Command after processCommand : " + command);


    }

    private void processCommand() throws InterruptedException {
        this.command = "xyz";

    }
}

さて、同期の問題が発生することを期待していますよね? 基本的に、いつ

System.out.println(Thread.currentThread().getName()+' Start. Command = '+command);

実行すると、値を取得できますxyzね?しかし、私はそれを見ることはありません。Thread.Sleep でさまざまな値を試しました。

this.command = "xyz";では、この場合、ステートメントをスレッドセーフにするのは何でしょうか?

私はこのようにスレッドを開始しています -

ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
    Runnable worker = new WorkerThread("" + i);
    executor.execute(worker);
}
4

2 に答える 2

4

ここで競合状態が表示されない理由は

Runnable worker = new WorkerThread('' + i);

競合状態には、共有リソースが関係しています。一方、すべてのワーカー スレッドは、独自のプライベート メンバーを変更していcommandます。競合状態を誘発するには、次のようなことをする必要があります

for (int i = 0; i < 10; i++) {
  Runnable worker = new WorkerThread('' + 0);    
  executor.execute(worker);
  worker.setCommand('' + i);
}

ワーカーがcommandフィールドにアクセスしようとすると、古い0値または値が取得される可能性がありiます。

于 2013-05-31T23:43:40.523 に答える
4

アップデート

完全なプログラムがどのように見えるかはまだ完全ではありません...しかし、私が考えていることに基づいて、スレッドセーフではない点は見当たりません。

command割り当てられる 2 つのポイントと、値が読み取られる 2 つのポイントがあります。

  1. メインスレッドcommandはコンストラクターで割り当てます。
  2. 2 番目のスレッドcommandは、run()を呼び出す前に読み込みますprocessCommand
  3. 2 番目のスレッドcommandprocessCommand
  4. 2 番目のスレッドcommandrun()、 を呼び出した後に読み込みますprocessCommand

最後の 3 つのイベントは同じスレッドで発生するため、同期は必要ありません。1 番目と 2 番目のイベントは異なるスレッドで発生しますが、その時点でメイン スレッドとワーカー スレッドの間に「前に発生する」関係があるはずです。

  • メイン スレッドがstart()2 番目のスレッドにある場合、それは事前発生を提供します。(JLSはそう言っています。)

  • しかし、実際にはThreadPoolExecutor.execute(Runnable)ハンドオーバーを行うために使用しており、javadocによるとExecutor:

    メモリー一貫性効果: Runnable オブジェクトを Executor に送信する前のスレッド内のアクションは、おそらく別のスレッドで実行が開始される前に発生します。

要約すると、対象の 4 つのイベントはすべて適切に同期されており、競合状態はありませんcommand


ただし、これがスレッドセーフでなかったとしても、その非スレッドセーフな動作を示すのは困難です。

  • それを実証できない主な理由は、実際の非安全性が Java メモリ モデルによるものであることです。変数への変更は、command同期ポイントまたは「前に起こる」ことを確立する何かがある場合にのみ、メイン メモリにフラッシュする必要があります。しかし、とにかくフラッシュすることができます...そして通常は...特に十分な時間のギャップがある場合、またはコンテキストスイッチを引き起こすシステムコールがある場合. この場合、両方を持っています。

  • 2 番目の理由は、オブジェクトSystem.errSystem.outオブジェクトが内部的に同期されているためです。それらの呼び出し方法に注意しないと、実証しようとしているスレッド セーフの問題を解消できます。


これは、共有変数への非同期アクセスに関連するスレッドセーフの問題に関する「問題」です。実際の競合状態には、多くの場合、非常に短い時間枠が含まれます。つまり、レースが注目されるためには、数クロック サイクル (確かに 1 マイクロ秒未満) 内に発生する必要がある2 つのイベントです。これはめったに発生しない可能性が高いため、競合状態を含む問題は通常、再現が非常に困難です。

于 2013-05-31T23:43:53.930 に答える