Firstly note that on some platforms AtomicLong
is implemented with a lock, so you may see a significant variation in performance.
You appear to be attempting to update two variables at once. Although many modern processors support that, the Java library does not. The version with a lock is trivial, so I'll elide that. You may also want to calculate the average on the get, and just keep a running sum and total, but I'll ignore that for the moment.
The most literal implementation would be using an AtomicReference
to an immutable value. Note this is going to cause an allocation, so may have great performance, particularly under low contention.
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));
}
Possibly a better solution is to keep a thread local (preferably not ThreadLocal
but an instance you've given to a particular thread to mutate). That can very quickly be locked and unlocked, because it'll be from the same thread. A thread infrequently requiring an average can then lock and read/read current values from all threads.
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);
}
}
(Disclaimer: Not checked or tested or compiled.)
... / (total++)
. Is that what you want? i.e. you are incrementing theAtomicLong
but nottotal
in your example operation. – Disruptivetotal.incrementAndGet()
. – Disruptive