kopia lustrzana https://github.com/onthegomap/planetiler
105 wiersze
3.0 KiB
Java
105 wiersze
3.0 KiB
Java
package com.onthegomap.planetiler.stats;
|
|
|
|
import java.util.List;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import java.util.function.LongSupplier;
|
|
|
|
/**
|
|
* A {@code long} value that can go up and down.
|
|
* <p>
|
|
* Incrementing an {@link AtomicLong} is fast from a single thread, but can be a bottleneck when multiple threads try to
|
|
* increment it at the same time, so this utility provides a {@link MultiThreadCounter} that gives out counters to each
|
|
* thread and adds up the total on read.
|
|
*/
|
|
public interface Counter {
|
|
|
|
default void inc() {
|
|
incBy(1);
|
|
}
|
|
|
|
void incBy(long value);
|
|
|
|
/** A counter that lets clients get the current value. */
|
|
interface Readable extends Counter, LongSupplier {
|
|
|
|
long get();
|
|
|
|
@Override
|
|
default long getAsLong() {
|
|
return get();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a counter that is optimized for updates from a single thread, but still thread safe for reads and writes
|
|
* from multiple threads.
|
|
*/
|
|
static Readable newSingleThreadCounter() {
|
|
return new SingleThreadCounter();
|
|
}
|
|
|
|
/**
|
|
* Returns a counter optimized for updates from multiple threads, and infrequent reads from a different thread.
|
|
*/
|
|
static MultiThreadCounter newMultiThreadCounter() {
|
|
return new MultiThreadCounter();
|
|
}
|
|
|
|
/**
|
|
* Counter optimized for updates from a single thread, but still safe for reads and writes from multiple threads.
|
|
*/
|
|
class SingleThreadCounter implements Readable {
|
|
|
|
private SingleThreadCounter() {}
|
|
|
|
private final AtomicLong counter = new AtomicLong(0);
|
|
|
|
@Override
|
|
public void incBy(long value) {
|
|
counter.addAndGet(value);
|
|
}
|
|
|
|
@Override
|
|
public long get() {
|
|
return counter.get();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Counter optimized for updates from a multiple threads and infrequent reads from other threads.
|
|
* <p>
|
|
* Threads should get a counter to update using {@link #counterForThread()} once and update the that instead of
|
|
* calling {@link #inc()} or {@link #incBy(long)} to avoid the cost of a thread-local lookup.
|
|
*/
|
|
class MultiThreadCounter implements Readable {
|
|
|
|
private MultiThreadCounter() {}
|
|
|
|
// keep track of all counters that have been handed out to threads so far
|
|
// and on read, add up the counts from each
|
|
private final List<SingleThreadCounter> all = new CopyOnWriteArrayList<>();
|
|
private final ThreadLocal<SingleThreadCounter> thread = ThreadLocal.withInitial(() -> {
|
|
SingleThreadCounter counter = new SingleThreadCounter();
|
|
all.add(counter);
|
|
return counter;
|
|
});
|
|
|
|
/** Don't use this, get a counter from {@link #counterForThread()} once and use that. */
|
|
@Override
|
|
public void incBy(long value) {
|
|
thread.get().incBy(value);
|
|
}
|
|
|
|
/** Returns the counter for this thread, so it can be cached to avoid subsequent thread local lookups. */
|
|
public Counter counterForThread() {
|
|
return thread.get();
|
|
}
|
|
|
|
@Override
|
|
public long get() {
|
|
return all.stream().mapToLong(SingleThreadCounter::get).sum();
|
|
}
|
|
}
|
|
}
|