Xen*_*non 5 java multithreading metrics dropwizard codahale-metrics
我最近开始学习 CodaHale/DropWizard 指标库。我无法理解 Meter 类如何是线程安全的(根据文档),尤其是这里的 mark() 和 tickIfNecessary() 方法:
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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以看到有一个 AtomicLong 类型的lastTick,但仍然可能存在 m1-m15 速率滴答时间稍长的情况,因此另一个线程可以调用这些滴答以及下一个 TICK_INTERVAL 的一部分。由于 Rates 的 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;
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢,
玛丽安
据我所知你是对的。如果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;
}
}
Run Code Online (Sandbox Code Playgroud)
alpha仅interval在实例初始化时设置,并标记为final,因为只读,所以线程安全。count并且instantRate是本地的并且无论如何对其他线程都是不可见的。rate并被initialized标记为易失性,并且这些写入对于后续读取应该始终可见。
如果我没记错的话,几乎从第一次读取initialized到最后一次写入,initialized或者rate这对竞争开放,但有些没有效果,比如当 2 个线程竞争切换initializedto时true。
似乎大多数有效的竞赛都可以在rate += (alpha * (instantRate - rate));特别下降或混合的计算中发生,例如:
initialized是truecount、instantRate检查、执行我们调用的initialized第一次读取,并且由于某种原因停止rateprevious_ratecount、instantRate检查initialized、计算rate += (alpha * (instantRate - rate));rate += (alpha * (instantRate - previous_rate));如果读取和写入以某种方式排序,以便rate在所有线程上读取,然后在所有线程上写入,从而有效地丢弃一个或多个计算,则会发生丢弃。
但是这种竞争的概率,意味着两个age > TICK_INTERVAL线程都遇到相同的tick()方法,尤其是rate += (alpha * (instantRate - rate))可能非常低,并且取决于不明显的值。
mark()只要对/和中的方法LongAdderProxy使用线程安全的数据结构,该方法似乎就是线程安全的。updateaddtick()sumThenReset
我认为唯一能够回答悬而未决的问题的人——无论竞赛没有明显的影响还是以其他方式减轻——是项目作者或对项目的这些部分和计算值有深入了解的人。
| 归档时间: |
|
| 查看次数: |
2496 次 |
| 最近记录: |