我需要执行以下操作:
// average, total, elapsed are Long's
average = ( ( total * average ) + elapsed ) / (++total);
但我想用AtomicLong
这就是我想,但我不完全得到,如果它是正确的:
average.set( (( total.get() * average.get() ) + elapsed) / total.getAndIncrement() );
我怎样才能知道,如果这是正确的吗?
大概你正在使用的AtomicLong,因为这些数字正在同时访问。 既然你有涉及到两个数字,你是在同一个语句中使用同时具有get和incrementAndGet,我不认为AtomicLong的是正确的选择。
我发现AtomicXXX是在很多情况下非常有用。 但在这里,我想你需要做的是硬盘的方式。 让您的数字简单的“长”私有变量,创建一个警卫对象,然后确保您访问人数任何时候保护对象上同步。
我认为这是你可以肯定的是,这些操作是真正的原子的唯一途径。
首先请注意,在某些平台上AtomicLong
用锁来实现,所以你可能会看到在性能显著变化。
你似乎是试图一次更新两个变量。 尽管许多现代的处理器支持,Java库没有。 有锁版本是微不足道的,所以我会说的Elid。 您可能还需要计算在获得平均,只是保持运行总和与总,但我会忽略的时刻。
最字面实施将使用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);
}
}
(声明:不检查或测试或编译)。
的假设是,这些计算将要由多个线程同时调用。 我最初并没有采取生效。
如果你想使用AtomicLong
做增量预先计算,那么你应该这样做:
long value = total.getAndIncrement();
average.set((value * average.get()) + elapsed) / (value + 1));
这仍然具有竞争条件然而由于平均可能是因为有人在其他人之间进行更新average.get()
和average.set()
这将不予以考虑效果的更新调用。
要完全肯定的是,你需要(如在他们的答复中提到@ user1657364)锁定周围的防护对象。
在你的作业总可能在第一和总总++不同。 你需要同步的整个操作。