首先请注意,在某些平台上AtomicLong
是使用锁实现的,因此您可能会看到性能上的显着变化。
您似乎试图一次更新两个变量。尽管许多现代处理器都支持这一点,但 Java 库不支持。带锁的版本是微不足道的,所以我会省略它。您可能还想计算 get 的平均值,并保持运行总和和总计,但我暂时忽略它。
最直接的实现是使用AtomicReference
不可变值。请注意,这将导致分配,因此可能具有出色的性能,尤其是在低争用情况下。
final class Average { // Find a better name...
private final long average;
private final long total;
public Average(long average, long total) {
this.average = average
this.total = total;
}
public long average() {
return average;
}
public long total() {
return total;
}
}
...
private final AtomicReference<Average> averageRef = new AtomicReference<>();
private void elapsed(final long elapsed) {
Average prev;
Average next;
do {
prev = average.get();
next = new Average(
((prev.total() * prev.average()) + elapsed ) / (prev.total() + 1),
prev.total() + 1
);
} while (!average.compareAndSet(prev, next));
}
可能更好的解决方案是将线程保持在本地(最好不是ThreadLocal
,而是您提供给特定线程以进行变异的实例)。这可以很快被锁定和解锁,因为它将来自同一个线程。然后,不经常需要平均值的线程可以锁定并从所有线程读取/读取当前值。
class Average { // Choose better name
private long sum;
private long total;
public synchronized void elapsed(final long elapsed) {
sum += elapsed;
++total;
}
public static long average(Iterable<Average> averages) {
long sum = 0;
long total = 0;
for (Average average : averages) {
synchronized (average) {
sum += averages.sum;
total += average.total;
}
}
return total==0 ? 0 : (sum/total);
}
}
(免责声明:未经检查、测试或编译。)