2017-06-08 10 views
2

私は最近CodaHale/DropWizardメトリクスライブラリを学び始めました。私があることがわかりますcodahale metricsのメーターマーク()メソッドはどのようにスレッドセーフですか?

https://github.com/dropwizard/metrics/blob/3.2-development/metrics-core/src/main/java/com/codahale/metrics/Meter.java#L54-L77

public void mark(long n) { 
    tickIfNecessary(); 
    count.add(n); 
    m1Rate.update(n); 
    m5Rate.update(n); 
    m15Rate.update(n); 
} 

private void tickIfNecessary() { 
    final long oldTick = lastTick.get(); 
    final long newTick = clock.getTick(); 
    final long age = newTick - oldTick; 
    if (age > TICK_INTERVAL) { 
     final long newIntervalStartTick = newTick - age % TICK_INTERVAL; 
     if (lastTick.compareAndSet(oldTick, newIntervalStartTick)) { 
      final long requiredTicks = age/TICK_INTERVAL; 
      for (long i = 0; i < requiredTicks; i++) { 
       m1Rate.tick(); 
       m5Rate.tick(); 
       m15Rate.tick(); 
      } 
     } 
    } 
} 

:私はメータークラスのスレッドセーフ(それはマニュアルに従っている)、特にマーク(あるかを理解)、ここでtickIfNecessary()メソッドはできませんlastTick型のAtomicLongがありますが、m1-m15のレートがちょっと遅くなっているので、別のスレッドが次のTICK_INTERVALの一部と同様にそれらのティックを呼び出すことができます。 tickのtick()メソッドは全く同期していないので、それは競合状態にならないでしょうか? https://github.com/dropwizard/metrics/blob/3.2-development/metrics-core/src/main/java/com/codahale/metrics/EWMA.java#L86-L95

public void tick() { 
    final long count = uncounted.sumThenReset(); 
    final double instantRate = count/interval; 
    if (initialized) { 
     rate += (alpha * (instantRate - rate)); 
    } else { 
     rate = instantRate; 
     initialized = true; 
    } 
} 

限り、私はあなたが正しいと見ることができるようおかげで、

マリアン

答えて

1

tickIfNecessary()が呼び出され、別の呼び出しが実行されている間にage > TICK_INTERVALが呼び出された場合、m1Rate.tick()と他のtick()のメソッドが複数のスレッドから同時に呼び出される可能性があります。だから、それは暖かいtick()に至り、呼び出されるルーチン/操作は安全です。

のはtick()を分析してみましょう:

public void tick() { 
    final long count = uncounted.sumThenReset(); 
    final double instantRate = count/interval; 
    if (initialized) { 
     rate += (alpha * (instantRate - rate)); 
    } else { 
     rate = instantRate; 
     initialized = true; 
    } 
} 

alphaintervalは、インスタンスの初期化時に設定され、読み取り専用のため、最終スレッドセーフそれらをマークされています。 countinstantRateはローカルであり、とにかく他のスレッドから見えないものです。 rateおよびinitializedと表示され、揮発性はであり、これらの書き込みは、読み取りに常に表示する必要があります。

私はinitializedまたはrateのいずれかで最後の書き込みにinitializedの最初の読み取りから、ほとんど間違っていないよ場合、これはレースのために開いているが、いくつかはtrueinitializedのスイッチのとき、2つのスレッドのレースのような効果なしです。

それは効果的なレースの大半はrate += (alpha * (instantRate - rate));に起こることができ、特に落下など混合計算のようだ:

  1. が想定:initializedtrue
  2. スレッド1である:、countを計算instantRateinitializedをチェックし、最初の読み取りを行いますrateの番号previous_rateと何らかの理由でストールします
  3. スレッド2:計算するcountinstantRate、チェックinitialized、およびrate += (alpha * (instantRate - rate));
  4. にスレッド1を計算します。その動作を継続して読み込み、何とかrateがすべてのスレッド上で読み、すべてのスレッドに書かれているように順序付けを取得書き込むとドロップが発生するrate += (alpha * (instantRate - previous_rate));

を計算し、 1つまたは複数の計算を効果的に削除します。

しかし、このようなレースのための確率は、2つの同じtick()方法に実行スレッドと特にrate += (alpha * (instantRate - rate))が極端に低いと目立たない値に依存してもよいこと、その両方age > TICK_INTERVAL試合は、そのような意味。

mark()方法であればLongAdderProxyupdate/addおよびsumThenResettick()メソッドのスレッドセーフデータ構造を使用するように、スレッドセーフであると思われます。

質問に答えることができる人は、レースが顕著な効果もなく軽減されていなくても、プロジェクトの作者またはプロジェクトのこれらの部分と計算された値の深い知識を持っている人です。

+0

これまでのところ、私はそれを開いたままにしていますので、著者の誰かがこれに気付くかもしれません。 – Xenon

1

ためtickIfNecessary()戻ってからこの行が真一度だけnewIntervalStartTick

if (lastTick.compareAndSet(oldTick, newIntervalStartTick)) 

あたりのそれは2つのスレッドがほぼ同時にtickIfNecessary()を入力した場合はどうなりますか?スレッドセーフですか

どちらのスレッドもoldTickと同じ値を読み取り、少なくともTICK_INTERVALナノ秒が経過したと判断し、newIntervalStartTickを計算します。

ここで両方のスレッドがlastTick.compareAndSet(oldTick, newIntervalStartTick)を実行しようとしています。 compareAndSetの意味するように、このメソッドは現在の値lastTickoldTickを比較し、値がoldTickに等しい場合にのみ、アトミックにnewIntervalStartTickに置き換えられ、trueを返します。

これはアトミック命令です(ハードウェアレベルで!)ので、1つのスレッドしか成功することはできません。他のスレッドがこのメソッドを実行すると、newIntervalStartTickが現在の値のlastTickとして既に表示されます。この値はoldTickと一致しなくなるため、更新は失敗し、このメソッドはfalseを返します。したがって、このスレッドはm1Rate.tick()からm15Rate.tick()を呼び出しません。

EWMA.update(n)メソッドは、java.util.concurrent.atomic.LongAdderを使用して、同様のスレッド安全性を保証するイベントカウントを累積します。

+0

OK何らかの理由でcompareAndSetの後にif部分でスレッドがハング/ウェイトして別のティックが発生した場合はどうなりますか?また、別のスレッドはcompareAndSetの後にif部分を入力します。私はそれが起こる可能性は低いと考えています。右? – Xenon

+0

あなたは私のコメントに答えることができますか^^、よろしいですか?ありがとう。 – Xenon

+1

これは起こることがありますが、ダニが5秒ごとに発生するため、**非常にありそうもありません。 –

関連する問題