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. 它更简单,可立即识别,并且很可能足够快。

如果只有一个线程更新计数器,则addAndGet没有争议,并且将与您的 Stats1 代码一样快。如果有多个线程更新,那么 Stats1 就是完全错误的。无论哪种方式,您都不应该使代码复杂化,除非您有基准测试/分析数据表明这对您的应用程序的整体运行时间来说是一笔巨大的成本。

于 2014-08-19T17:09:55.990 回答
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 公开了多少变量,单个原子引用都可以控制所有变量。

于 2014-08-19T18:08:41.613 回答
1

如果您使用的是 Java 8,则应考虑 java.util.concurrent.atomic 中的LongAdder类:

当多个线程更新用于收集统计信息等目的的公共总和时,此类通常比 AtomicLong 更可取,而不是用于细粒度的同步控制。在低更新争用下,这两个类具有相似的特征。但是在高竞争下,这个类的预期吞吐量明显更高,代价是更高的空间消耗。

请注意,如果您遵循从不同步的已接受答案中的建议,那么统计信息可能永远不会更新(取决于您的程序中发生的其他事情,您正在运行的架构,哪个 Java 版本等。 )。

于 2014-08-19T18:40:13.910 回答
0

在单线程环境中,您可以使用 lazySet 。getAndSet 主要用于多线程环境

于 2014-08-19T17:01:53.710 回答