我有一个带有单个编写器线程的应用程序,它执行一些操作并更新一些指标,例如应用程序有许多其他线程可以读取统计信息并使用它们执行操作。指标是最新的并不是必不可少的,但编写器线程需要在尽可能短的时间内被阻止。我想知道以下哪一项更适合我的需求:
选项1 - 具有只有作者可以更新的非原子字段,以及使用lazySet
设置的AtomicXXX字段:
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 - 只需拥有一个通过addAndGet
更新的AtomicXXX字段:
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();
}
}
或者我会做些什么更好?
提前致谢。
答案 0 :(得分:3)
只需使用addAndGet
即可。它更简单,可以立即识别,而且可能足够快。
如果只有一个线程更新了计数器,则addAndGet
无争议,并且速度与Stats1代码一样快。如果有多个线程更新,那么Stats1就是完全错误的。无论哪种方式,您都不应该使代码复杂化,除非您有基准/分析数据表明它对您的应用程序的整体运行时间来说是一笔巨大的成本。
答案 1 :(得分: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暴露了多少变量,单个原子引用都可以控制所有这些变量。
答案 2 :(得分:1)
如果您使用的是Java 8,则应该考虑java.util.concurrent.atomic中的LongAdder类:
当多个线程更新用于收集统计信息但不用于细粒度同步控制的目的的公共和时,此类通常优于AtomicLong。在低更新争用下,这两个类具有相似的特征。但在高度争用的情况下,这一类的预期吞吐量明显更高,但代价是空间消耗更高。
请注意,如果您遵循接受的永不同步的答案中的建议,则统计信息可能会永不更新(取决于您的计划中发生了什么,您采用的是哪种架构)重新运行,哪个Java版本等。)。
答案 3 :(得分:0)
在单线程环境中,您可以使用lazySet。 getAndSet主要用于多线程环境