最初に、一部のプラットフォームAtomicLong
では がロック付きで実装されているため、パフォーマンスに大きなばらつきが見られる場合があることに注意してください。
一度に 2 つの変数を更新しようとしているようです。最近のプロセッサの多くはこれをサポートしていますが、Java ライブラリはサポートしていません。ロック付きのバージョンは些細なことなので省略します。また、取得時に平均を計算して、現在の合計と合計を保持することもできますが、ここではそれを無視します。
最も文字通りの実装は、AtomicReference
to を不変の値に使用することです。これにより割り当てが発生するため、特に競合が少ない場合は、優れたパフォーマンスが得られる可能性があることに注意してください。
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);
}
}
(免責事項:チェック、テスト、またはコンパイルされていません。)