3

この質問は、2つのブログ投稿( http://dow.ngra.de/2008/10/27/when-systemcurrenttimemillis-is-too-slow/、http://dow.ngra.de/2008/10)で議論されています。 / 28 / what-do-we-really-know-about-non-blocking-concurrency-in-java /)、しかし私はまだ決定的な答えを聞いていません。これを行うスレッドが1つある場合:

public class HeartBeatThread extends Thread {
  public static int counter = 0;
  public static volatile int cacheFlush = 0;

  public HeartBeatThread() {
    setDaemon(true);
  }

  static {
    new HeartBeatThread().start();
  }

  public void run() {   
    while (true) {     
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }

      counter++;
      cacheFlush++;
    }
  }
}

そして、以下を実行する多くのクライアント:

if (counter == HeartBeatThread.counter) return;
counter = HeartBeatThread.cacheFlush;

スレッドセーフかどうか?

4

2 に答える 2

5

Javaメモリモデル内? いいえ、あなたは大丈夫ではありません。

このような非常に「ソフトなフラッシュ」アプローチに向かう試みを数多く見てきましたが、明示的なフェンスがなければ、間違いなく火遊びをしていることになります。

の「前に起こる」セマンティクス

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.7

17.4.2 の最後に、純粋にスレッド間アクションを「アクション」と呼び始めます。それ以前は、スレッド間アクションとスレッド内アクションを区別していたため、これは多くの混乱を引き起こします。その結果、カウンターを操作するスレッド内アクションは、前発生の関係によって揮発性アクション全体で明示的に同期されません。同期について従うべき推論の 2 つのスレッドがあり、1 つはローカルの一貫性を管理し、操作をシャッフルするためのエイリアス分析などのすべての優れたトリックの対象となります。もう 1 つはグローバルな一貫性に関するもので、スレッド間操作に対してのみ定義されます。

1 つはスレッド内で読み取りと書き込みが一貫して並べ替えられることを示すスレッド内ロジック用で、もう 1 つは揮発性の読み取り/書き込みや同期の開始/終了が適切にフェンスされていることを示すスレッド間ロジック用です。

問題は、不揮発性書き込みの可視性がスレッド内操作であるため未定義であるため、仕様でカバーされていないことです。実行中のプロセッサは、これらのステートメントをシリアルに実行したときにそれを認識できるはずですが、スレッド間の目的での順次化は潜在的に未定義です。

さて、これがあなたに影響を与えるかどうかという現実は、まったく別の問題です.

x86 および x86-64 プラットフォームで Java を実行中ですか? 技術的にはあなたは曖昧な領域にいますが、実際には非常に強力な x86 が読み取りと書き込みに配置することを保証します。これには、キャッシュフラッシュへのアクセス全体の読み取り/書き込みの合計順序と、2 つの書き込みと 2 つの読み取りのローカル順序が含まれ、このコードを有効にする必要があります。コンパイラを邪魔されずに通り抜けることができれば、正しく実行できます。これは、コンパイラが介入せず、標準で許可されている自由を使用して操作を並べ替えようとしないことを前提としています。これは、2 つのスレッド内操作間のエイリアシングが証明されていないためです。

ia64 のようなリリース セマンティクスの弱いメモリに移動する場合は? その後、あなたは自分自身に戻っています。

ただし、コンパイラは、完全に善意で、どのプラットフォームの Java でもこのプログラムを壊す可能性があります。現在機能しているのは、標準ではなく、標準の現在の実装の成果物です。

余談ですが、CLR ではランタイム モデルがより強力であり、各スレッドからの個々の書き込みが可視性を順序付けられているため、この種のトリックは合法です。

于 2008-11-07T21:33:06.527 に答える
1

まあ、そうではないと思います。

最初のifステートメント:

if (counter == HeartBeatThread.counter) 
    return;

揮発性フィールドにアクセスせず、同期されません。そのため、古いデータを永久に読み取り、揮発性フィールドにアクセスするポイントに到達しない可能性があります。

2番目のブログエントリのコメントの1つから引用すると、「揮発性フィールドfに書き込むときにスレッドAに表示されていたものはすべて、fを読み取るときにスレッドBに表示されるようになります。」しかし、あなたの場合、B(クライアント)はf(= cacheFlush)を決して読み取りません。したがって、HeartBeatThread.counterへの変更は、クライアントに表示される必要はありません。

于 2008-11-07T21:27:34.477 に答える