0

Javaでcuncurrent読み取り/アトミック書き込みを実装しようとしています。

static int atom = 0;

static boolean flag = false;

public static void main(String[] args) {

  new Thread(new Reader()).start();
  new Thread(new Reader()).start();
  new Thread(new Reader()).start();

  Timer timer = new Timer();
  timer.schedule(new TimerTask() {
    @Override
    public void run() {
      write();
    }
  }, 3000);
}

static void write() {
  flag = true;

  Thread.sleep(1000);
  atom++;

  flag = false;
}

static class Reader implements Runnable {

  @Override
  public void run() {
    while (true) {
      Thread.sleep(5);

      if (flag) {
        continue;
      }

      System.out.println(atom);
    }
  }
}

私のスレッドがatom varを読み取ると、フラグがtrueとマークされるまで読み取りが行われ、フラグがオフになると値が変更された後も読み取りが続行されます。

これを行う最善の方法は何ですか?同期ブロックを使用していますか?

ありがとう、フェドラ。

4

4 に答える 4

3

マルチパートの回答。パート 1 では、実際の質問に答えます。パート 2 では、少し編集します。


ここにあるのは、データ競合を含むあらゆる種類の際どいものです。発生する前の順序がないため、JIT はこれを有効にする権利の範囲内に完全に収まります。

while(true) {
    ...
    if (flag) {...}
}

これに:

boolean flagCache = flag;
while(true) {
    if (flagCache) { ... }
}

flagCache2 番目のバージョンでは更新されないことに注意してください。flagとしてマークされていないため、JVM はそれを更新する義務はありませんvolatile

それを超えて、write気づいたように見える競合状態があり、synchronizedブロックが実際に役立つ可能性があります。アプローチは、別のものを作成してから、メソッドprivate static final Object lock = new Object()内で同期することです。writeそうすれば、書き込みはそのオブジェクトで同期されますが、読み取りは同期されません。このルートに行く場合は、atomとしてもマークする必要がありますvolatile。そして、特にブロック内ではThread.sleep(1000)、そのメソッドでは絶対に必要ではありません。書き込みに何らかの種類の同時実行性がある場合、これは大きなボトルネックになります。writesynchronized

atom(正しいことを行う場合、 as としてマークすることvolatileは実際には厳密には必要ありません [ from を読み取ることでvolatile flag得られる事前発生保証に便乗することができます]。また、JMM/並行性の点で上級プログラマーであると考えてください。)

ボトルネックについて言えば、Thread.sleep(5)忙しい待機ループの中で、興味深いトレードオフとして言及する価値があります。これがないと、読み取りスレッドが大量にスピンし、CPU を浪費する可能性があります。しかし、それでは、理由もなく回転しすぎる可能性があります。flagしかし、実際には、まったく、またはビジー待機ループは必要ありません。atomとしてマークしvolatileて書き込みを同期するだけで、読み取りは正常に行われます。揮発性フィールドへの書き込みとそこからの読み取りの間には、先行発生エッジがあります。


しかし、@Damian Jeżewskiはここにいると思います。AnAtomicIntegerは、簡単に、簡単に、より効率的にトリックを実行します (ブロックする可能性のあるコンテキスト切り替えブロックの代わりに比較と設定synchronizedを使用するため)。

マルチスレッド化への一般的なアプローチは、ほとんどの場合、java.util.concurrent.*低レベルのもの (volatileまたはのようなものsynchronized、そして間違いなく の前Object.wait/notify) を使用する前に、高レベルの構成 (つまり、 内のもの) を試して使用することです。もちろん、これは厳格なルールではありません。一般的なガイドラインにすぎません。AtomicInteger.incrementAndGetこの場合、非常に単純な要件 (スレッドセーフな書き込みと同時読み取りの高度化) を「無料」で提供するために、どれだけの考慮が必要だったかを見てください。

于 2013-03-18T15:59:54.867 に答える
2

パッケージAtomicIntegerから使ったほうがいいんじゃない?java.util.concurrent.atomic

于 2013-03-18T15:43:50.100 に答える
1

クラスで行うか、変数としてsynchronize宣言する必要があります。そうしないと、1 つのスレッドによって実行された同時変更が他のスレッドで表示されることが保証されません。flagatomvolatile

isCountDownLatchまで待つ代わりに、カウント 1 で a を使用することもできます。flagtrue

于 2013-03-18T15:45:04.167 に答える
0

コードの問題。1. atom++ は、こちらで説明されているように競合状態を導入します 2.ライターによって更新されたフラグ値は、リーダーにすぐに表示されない場合があります 3.ライターによって更新されたアトム値は、リーダーにすぐに表示されない場合があります

スレッドセーフにするために必要な 2 つの簡単な手順を次に示します。

  1. ダミアンが指摘したように、アトムをAtomicInteger にします。
  2. マークフラグ揮発性
于 2013-10-05T17:10:40.877 に答える