High Dynamic Range (HDR) Histogram for recording and analyzing value distributions with configurable precision across wide dynamic ranges.
—
Thread-safe histogram implementations providing different levels of concurrency support for multi-threaded environments. Each variant offers distinct trade-offs between performance, thread safety guarantees, and feature support.
Lock-free, wait-free histogram using atomic operations for count updates. Provides thread-safe recording with limited synchronization for other operations.
public class AtomicHistogram extends Histogram {
// Constructors
public AtomicHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
public AtomicHistogram(long lowestDiscernibleValue,
long highestTrackableValue,
int numberOfSignificantValueDigits);
public AtomicHistogram(AbstractHistogram source);
// Factory methods
static AtomicHistogram decodeFromByteBuffer(ByteBuffer buffer,
long minBarForHighestTrackableValue);
static AtomicHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer,
long minBarForHighestTrackableValue);
static AtomicHistogram fromString(String base64CompressedHistogramString);
// Implementation methods
public AtomicHistogram copy();
public AtomicHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
}Thread-Safe Operations:
recordValue() - Wait-free atomic recordingrecordValueWithCount() - Wait-free atomic recordingrecordValueWithExpectedInterval() - Wait-free atomic recordingNOT Thread-Safe Operations:
// Create atomic histogram (no auto-resize support)
AtomicHistogram histogram = new AtomicHistogram(1_000_000, 3);
// Thread-safe recording from multiple threads
// Thread 1
new Thread(() -> {
for (int i = 0; i < 10000; i++) {
histogram.recordValue(ThreadLocalRandom.current().nextInt(1000));
}
}).start();
// Thread 2
new Thread(() -> {
for (int i = 0; i < 10000; i++) {
histogram.recordValue(ThreadLocalRandom.current().nextInt(2000));
}
}).start();
// Reading requires external synchronization
synchronized (histogram) {
long count = histogram.getTotalCount();
double mean = histogram.getMean();
long p95 = histogram.getValueAtPercentile(95.0);
}Fully thread-safe histogram supporting concurrent recording, auto-resizing, and value shifting operations using writer-reader phasing.
public class ConcurrentHistogram extends Histogram {
// Constructors
public ConcurrentHistogram(int numberOfSignificantValueDigits);
public ConcurrentHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
public ConcurrentHistogram(long lowestDiscernibleValue,
long highestTrackableValue,
int numberOfSignificantValueDigits);
public ConcurrentHistogram(AbstractHistogram source);
// Factory methods
static ConcurrentHistogram decodeFromByteBuffer(ByteBuffer buffer,
long minBarForHighestTrackableValue);
static ConcurrentHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer,
long minBarForHighestTrackableValue);
static ConcurrentHistogram fromString(String base64CompressedHistogramString);
// Implementation methods
public ConcurrentHistogram copy();
public ConcurrentHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
}ConcurrentHistogram automatically enables auto-resize functionality and always sets auto-resize to true, regardless of the constructor used. This ensures that the histogram can dynamically expand to accommodate values outside its initial range without requiring manual configuration.
// All ConcurrentHistogram instances have auto-resize enabled by default
ConcurrentHistogram histogram = new ConcurrentHistogram(3);
// Internal call: histogram.setAutoResize(true) - always executed
// Even if you try to disable it, it remains enabled
histogram.setAutoResize(false); // This call has no effect
assert histogram.isAutoResize() == true; // Always true for ConcurrentHistogramWait-Free/Lock-Free Operations:
recordValue() - Wait-free recordingrecordValueWithCount() - Wait-free recordingrecordValueWithExpectedInterval() - Wait-free recordingRequires External Synchronization:
add(), subtract(), etc.)// Create concurrent histogram with auto-resize support
ConcurrentHistogram histogram = new ConcurrentHistogram(3);
// Multiple threads can record concurrently without synchronization
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int t = 0; t < 10; t++) {
executor.submit(() -> {
Random random = new Random();
for (int i = 0; i < 100000; i++) {
// No synchronization needed for recording
histogram.recordValue(random.nextInt(10000));
}
});
}
// Auto-resizing works concurrently
histogram.recordValue(Long.MAX_VALUE / 4); // Will auto-resize if needed
// Queries still need external synchronization
synchronized (histogram) {
histogram.outputPercentileDistribution(System.out, 1.0);
}
executor.shutdown();ConcurrentHistogram uses a writer-reader phaser for coordination:
// Example of coordinated reading
WriterReaderPhaser phaser = histogram.getWriterReaderPhaser();
phaser.readerLock();
try {
// Safe to iterate or perform complex queries
for (HistogramIterationValue value : histogram.recordedValues()) {
System.out.println("Value: " + value.getValueIteratedTo() +
", Count: " + value.getCountAtValueIteratedTo());
}
} finally {
phaser.readerUnlock();
}Fully thread-safe histogram with synchronized access for all operations. All methods are atomic with respect to modifications.
public class SynchronizedHistogram extends Histogram {
// Constructors
public SynchronizedHistogram(int numberOfSignificantValueDigits);
public SynchronizedHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
public SynchronizedHistogram(long lowestDiscernibleValue,
long highestTrackableValue,
int numberOfSignificantValueDigits);
public SynchronizedHistogram(AbstractHistogram source);
// Factory methods
static SynchronizedHistogram decodeFromByteBuffer(ByteBuffer buffer,
long minBarForHighestTrackableValue);
static SynchronizedHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer,
long minBarForHighestTrackableValue);
// Implementation methods
public SynchronizedHistogram copy();
public SynchronizedHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
}Fully Synchronized Operations:
// Create synchronized histogram
SynchronizedHistogram histogram = new SynchronizedHistogram(3);
// All operations are thread-safe but may block
Runnable recorder = () -> {
for (int i = 0; i < 10000; i++) {
histogram.recordValue(i); // Thread-safe but blocking
}
};
Runnable analyzer = () -> {
while (true) {
// Thread-safe queries - no external synchronization needed
long count = histogram.getTotalCount();
double mean = histogram.getMean();
long p99 = histogram.getValueAtPercentile(99.0);
System.out.printf("Count: %d, Mean: %.2f, P99: %d%n", count, mean, p99);
try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
}
};
// Start concurrent operations
new Thread(recorder).start();
new Thread(recorder).start();
new Thread(analyzer).start();
// Complex operations are also thread-safe
SynchronizedHistogram other = new SynchronizedHistogram(3);
other.recordValue(1000);
histogram.add(other); // Thread-safe histogram addition| Feature | AtomicHistogram | ConcurrentHistogram | SynchronizedHistogram |
|---|---|---|---|
| Recording Performance | Excellent (wait-free) | Excellent (wait-free) | Good (blocking) |
| Auto-resize Support | No | Yes | Yes |
| Value Shifting | No | Yes | Yes |
| Query Thread Safety | External sync needed | External sync needed | Built-in |
| Memory Overhead | Low | Medium (phaser) | Low |
| Use Case | High-frequency recording | Full concurrent features | Simple thread safety |
// High-frequency latency recording
AtomicHistogram latencyHist = new AtomicHistogram(10_000_000, 3); // Max 10s latency
// Multiple producer threads
producers.forEach(producer ->
producer.onLatencyMeasured(latency -> latencyHist.recordValue(latency))
);
// Periodic analysis with synchronization
scheduledExecutor.scheduleAtFixedRate(() -> {
synchronized (latencyHist) {
analyzeLatencyDistribution(latencyHist);
}
}, 0, 10, TimeUnit.SECONDS);// Auto-resizing concurrent histogram for varying workloads
ConcurrentHistogram responseTime = new ConcurrentHistogram(3);
// Multiple threads recording different ranges
recordingThreads.forEach(thread ->
thread.onResponse(time -> responseTime.recordValue(time))
);
// Coordinated analysis
WriterReaderPhaser phaser = responseTime.getWriterReaderPhaser();
phaser.readerLock();
try {
generateReport(responseTime);
} finally {
phaser.readerUnlock();
}// Simple thread-safe histogram for mixed read/write workloads
SynchronizedHistogram requestSize = new SynchronizedHistogram(3);
// Recording thread
requestHandler.onRequest(req -> requestSize.recordValue(req.getSize()));
// Monitoring threads can query safely
monitoringThreads.forEach(monitor ->
monitor.scheduleReporting(() -> {
// No synchronization needed
double avgSize = requestSize.getMean();
long p95Size = requestSize.getValueAtPercentile(95.0);
reportMetrics(avgSize, p95Size);
})
);All concurrent variants provide proper memory visibility guarantees:
Install with Tessl CLI
npx tessl i tessl/maven-org-hdrhistogram--hdr-histogram