0

いくつかのことを行い、カウンターなどのいくつかのメトリックを更新する単一のライタースレッドを持つアプリケーションがあります。アプリケーションには、統計を読み取ってそれらを処理する他のスレッドが多数あります。メトリックが最新であることは必須ではありませんが、ライター スレッドがブロックされる時間をできるだけ短くする必要があります。私は、次のうちどれが私のニーズに適しているか疑問に思っていました。

オプション 1 - ライターのみが更新できる非アトミック フィールドと、以下を使用して設定される AtomicXXX フィールドがありますlazySet

class Stats1
{
  private final AtomicLong someCounterAtomic = new AtomicLong(0);
  private long someCounter = 0;

  // writer thread updates the stats using this
  public void incrementCounter(long counterIncrement)
  {
    someCounter += counterIncrement;
    someCounterAtomic.lazySet(someCounter);
  }

  // reader threads call this
  public long getCounterValue()
  {
    return someCounterAtomic.get();
  }
}

オプション 2 - 経由で更新される AtomicXXX フィールドがあるだけですaddAndGet

class Stats2
{
  private final AtomicLong someCounter = new AtomicLong(0);

  // writer thread updates the stats using this
  public void incrementCounter(long counterIncrement)
  {
    someCounter.addAndGet(counterIncrement);
  }

  // reader threads call this
  public long getCounterValue()
  {
    return someCounter.get();
  }
}

それとも、何か他のことをしたほうがいいでしょうか?

前もって感謝します。

4

4 に答える 4

3

を使用するだけaddAndGetです。それはよりシンプルで、すぐに認識でき、おそらく十分に高速です。

カウンターを更新するスレッドが 1 つしかない場合、addAndGet競合はなく、Stats1 コードとほぼ同じ速度になります。複数のスレッドが更新されている場合、Stats1 は単純に間違っています。いずれにせよ、アプリの全体的な実行時間に多大なコストがかかることを示唆するベンチマーク/プロファイリング データがない限り、コードを複雑にするべきではありません。

于 2014-08-19T17:09:55.990 に答える
1

Java 8 を使用している場合は、java.util.concurrent.atomic のLongAdderクラスを検討する必要があります。

通常、このクラスは、きめ細かな同期制御ではなく、統計の収集などの目的で使用される共通の合計を複数のスレッドが更新する場合に、AtomicLong よりも適しています。更新の競合が少ない場合、2 つのクラスは同様の特性を持っています。ただし、競合が多い場合、このクラスの予想スループットは大幅に高くなりますが、スペースの消費量が増えます。

同期しないという受け入れられた回答の提案に従うと、統計が更新されない可能性があることに注意してください(プログラムで他に何が起こっているか、実行しているアーキテクチャ、Javaのバージョンなどによって異なります。 )。

于 2014-08-19T18:40:13.910 に答える
1

ある程度最近の統計に問題がない場合は、何も同期しません。ライター スレッドで定期的にスナップショットを取得し、アトミック リファレンスを介して定期的に (または統計が十分に変化した後に) 公開します。

public class Stats {
     // the currently available stat instance
     public static AtomicReference<Stats> CURRENT_STATS = 
         new AtomicReference<>(new Stats());

     private final int whatever1;
     private final int whatever2;

     private Stats() {
         this(0, 0);
     }

     public Stats(int whatever1, int whatever2) {
         this.whatever1 = whatever1;
         ...
     }

     public int getWhatever1() {
         return whatever1;
     }

     public int getWhatever2() {
         return whatever2;
     }

}

ライターは、適切と判断したときに新しい Stats インスタンスを作成し、CURRENT_STATS 参照を設定します。リーダーは、統計が必要なときに参照を読み取り、現在の処理パスが完了するまで参照を保持します (処理中に統計が変更されるのを回避します)。

これには最小限の同期が必要であり、最新の統計の要件が緩い場合に適しています (たとえば、スループットなどをユーザーに表示するため)。Stats が公開する変数の数に関係なく、1 つのアトミック参照でそれらすべてを制御できます。

于 2014-08-19T18:08:41.613 に答える
0

シングルスレッド環境では、 lazySet を使用できます。getAndSet は主にマルチスレッド環境用です

于 2014-08-19T17:01:53.710 に答える