0
# Concurrent Histogram Variants
1
2
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.
3
4
## AtomicHistogram
5
6
Lock-free, wait-free histogram using atomic operations for count updates. Provides thread-safe recording with limited synchronization for other operations.
7
8
```java { .api }
9
public class AtomicHistogram extends Histogram {
10
11
// Constructors
12
public AtomicHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
13
public AtomicHistogram(long lowestDiscernibleValue,
14
long highestTrackableValue,
15
int numberOfSignificantValueDigits);
16
public AtomicHistogram(AbstractHistogram source);
17
18
// Factory methods
19
static AtomicHistogram decodeFromByteBuffer(ByteBuffer buffer,
20
long minBarForHighestTrackableValue);
21
static AtomicHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer,
22
long minBarForHighestTrackableValue);
23
static AtomicHistogram fromString(String base64CompressedHistogramString);
24
25
// Implementation methods
26
public AtomicHistogram copy();
27
public AtomicHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
28
}
29
```
30
31
### Thread Safety Characteristics
32
33
**Thread-Safe Operations:**
34
- `recordValue()` - Wait-free atomic recording
35
- `recordValueWithCount()` - Wait-free atomic recording
36
- `recordValueWithExpectedInterval()` - Wait-free atomic recording
37
38
**NOT Thread-Safe Operations:**
39
- Auto-resizing operations
40
- Value shifting operations
41
- Iteration operations
42
- Statistical queries (use external synchronization)
43
44
### Usage Examples
45
46
```java
47
// Create atomic histogram (no auto-resize support)
48
AtomicHistogram histogram = new AtomicHistogram(1_000_000, 3);
49
50
// Thread-safe recording from multiple threads
51
// Thread 1
52
new Thread(() -> {
53
for (int i = 0; i < 10000; i++) {
54
histogram.recordValue(ThreadLocalRandom.current().nextInt(1000));
55
}
56
}).start();
57
58
// Thread 2
59
new Thread(() -> {
60
for (int i = 0; i < 10000; i++) {
61
histogram.recordValue(ThreadLocalRandom.current().nextInt(2000));
62
}
63
}).start();
64
65
// Reading requires external synchronization
66
synchronized (histogram) {
67
long count = histogram.getTotalCount();
68
double mean = histogram.getMean();
69
long p95 = histogram.getValueAtPercentile(95.0);
70
}
71
```
72
73
### Performance Characteristics
74
75
- **Recording**: Wait-free, no contention between recording threads
76
- **Memory**: Atomic operations may have cache coherency overhead
77
- **Scalability**: Excellent for high-frequency recording scenarios
78
- **Limitations**: Fixed size (no auto-resize), queries need synchronization
79
80
## ConcurrentHistogram
81
82
Fully thread-safe histogram supporting concurrent recording, auto-resizing, and value shifting operations using writer-reader phasing.
83
84
```java { .api }
85
public class ConcurrentHistogram extends Histogram {
86
87
// Constructors
88
public ConcurrentHistogram(int numberOfSignificantValueDigits);
89
public ConcurrentHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
90
public ConcurrentHistogram(long lowestDiscernibleValue,
91
long highestTrackableValue,
92
int numberOfSignificantValueDigits);
93
public ConcurrentHistogram(AbstractHistogram source);
94
95
// Factory methods
96
static ConcurrentHistogram decodeFromByteBuffer(ByteBuffer buffer,
97
long minBarForHighestTrackableValue);
98
static ConcurrentHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer,
99
long minBarForHighestTrackableValue);
100
static ConcurrentHistogram fromString(String base64CompressedHistogramString);
101
102
// Implementation methods
103
public ConcurrentHistogram copy();
104
public ConcurrentHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
105
}
106
```
107
108
### Auto-Resize Configuration
109
110
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.
111
112
```java
113
// All ConcurrentHistogram instances have auto-resize enabled by default
114
ConcurrentHistogram histogram = new ConcurrentHistogram(3);
115
// Internal call: histogram.setAutoResize(true) - always executed
116
117
// Even if you try to disable it, it remains enabled
118
histogram.setAutoResize(false); // This call has no effect
119
assert histogram.isAutoResize() == true; // Always true for ConcurrentHistogram
120
```
121
122
### Thread Safety Characteristics
123
124
**Wait-Free/Lock-Free Operations:**
125
- `recordValue()` - Wait-free recording
126
- `recordValueWithCount()` - Wait-free recording
127
- `recordValueWithExpectedInterval()` - Wait-free recording
128
- Auto-resizing during recording
129
- Value shifting operations
130
131
**Requires External Synchronization:**
132
- Queries and statistical operations
133
- Iteration operations
134
- Histogram manipulation (`add()`, `subtract()`, etc.)
135
136
### Usage Examples
137
138
```java
139
// Create concurrent histogram with auto-resize support
140
ConcurrentHistogram histogram = new ConcurrentHistogram(3);
141
142
// Multiple threads can record concurrently without synchronization
143
ExecutorService executor = Executors.newFixedThreadPool(10);
144
145
for (int t = 0; t < 10; t++) {
146
executor.submit(() -> {
147
Random random = new Random();
148
for (int i = 0; i < 100000; i++) {
149
// No synchronization needed for recording
150
histogram.recordValue(random.nextInt(10000));
151
}
152
});
153
}
154
155
// Auto-resizing works concurrently
156
histogram.recordValue(Long.MAX_VALUE / 4); // Will auto-resize if needed
157
158
// Queries still need external synchronization
159
synchronized (histogram) {
160
histogram.outputPercentileDistribution(System.out, 1.0);
161
}
162
163
executor.shutdown();
164
```
165
166
### Writer-Reader Phasing
167
168
ConcurrentHistogram uses a writer-reader phaser for coordination:
169
170
```java
171
// Example of coordinated reading
172
WriterReaderPhaser phaser = histogram.getWriterReaderPhaser();
173
phaser.readerLock();
174
try {
175
// Safe to iterate or perform complex queries
176
for (HistogramIterationValue value : histogram.recordedValues()) {
177
System.out.println("Value: " + value.getValueIteratedTo() +
178
", Count: " + value.getCountAtValueIteratedTo());
179
}
180
} finally {
181
phaser.readerUnlock();
182
}
183
```
184
185
## SynchronizedHistogram
186
187
Fully thread-safe histogram with synchronized access for all operations. All methods are atomic with respect to modifications.
188
189
```java { .api }
190
public class SynchronizedHistogram extends Histogram {
191
192
// Constructors
193
public SynchronizedHistogram(int numberOfSignificantValueDigits);
194
public SynchronizedHistogram(long highestTrackableValue, int numberOfSignificantValueDigits);
195
public SynchronizedHistogram(long lowestDiscernibleValue,
196
long highestTrackableValue,
197
int numberOfSignificantValueDigits);
198
public SynchronizedHistogram(AbstractHistogram source);
199
200
// Factory methods
201
static SynchronizedHistogram decodeFromByteBuffer(ByteBuffer buffer,
202
long minBarForHighestTrackableValue);
203
static SynchronizedHistogram decodeFromCompressedByteBuffer(ByteBuffer buffer,
204
long minBarForHighestTrackableValue);
205
206
// Implementation methods
207
public SynchronizedHistogram copy();
208
public SynchronizedHistogram copyCorrectedForCoordinatedOmission(long expectedInterval);
209
}
210
```
211
212
### Thread Safety Characteristics
213
214
**Fully Synchronized Operations:**
215
- All operations are synchronized on the histogram instance
216
- Recording operations may block other operations
217
- Queries are consistent with concurrent modifications
218
- All histogram manipulation operations are thread-safe
219
220
### Usage Examples
221
222
```java
223
// Create synchronized histogram
224
SynchronizedHistogram histogram = new SynchronizedHistogram(3);
225
226
// All operations are thread-safe but may block
227
Runnable recorder = () -> {
228
for (int i = 0; i < 10000; i++) {
229
histogram.recordValue(i); // Thread-safe but blocking
230
}
231
};
232
233
Runnable analyzer = () -> {
234
while (true) {
235
// Thread-safe queries - no external synchronization needed
236
long count = histogram.getTotalCount();
237
double mean = histogram.getMean();
238
long p99 = histogram.getValueAtPercentile(99.0);
239
240
System.out.printf("Count: %d, Mean: %.2f, P99: %d%n", count, mean, p99);
241
242
try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
243
}
244
};
245
246
// Start concurrent operations
247
new Thread(recorder).start();
248
new Thread(recorder).start();
249
new Thread(analyzer).start();
250
251
// Complex operations are also thread-safe
252
SynchronizedHistogram other = new SynchronizedHistogram(3);
253
other.recordValue(1000);
254
histogram.add(other); // Thread-safe histogram addition
255
```
256
257
## Concurrency Comparison
258
259
| Feature | AtomicHistogram | ConcurrentHistogram | SynchronizedHistogram |
260
|---------|----------------|-------------------|---------------------|
261
| Recording Performance | Excellent (wait-free) | Excellent (wait-free) | Good (blocking) |
262
| Auto-resize Support | No | Yes | Yes |
263
| Value Shifting | No | Yes | Yes |
264
| Query Thread Safety | External sync needed | External sync needed | Built-in |
265
| Memory Overhead | Low | Medium (phaser) | Low |
266
| Use Case | High-frequency recording | Full concurrent features | Simple thread safety |
267
268
## Choosing the Right Variant
269
270
### Use AtomicHistogram When:
271
- High-frequency recording from multiple threads
272
- Fixed value range is acceptable
273
- Reading/analysis happens infrequently with controlled synchronization
274
- Maximum recording performance is critical
275
276
```java
277
// High-frequency latency recording
278
AtomicHistogram latencyHist = new AtomicHistogram(10_000_000, 3); // Max 10s latency
279
280
// Multiple producer threads
281
producers.forEach(producer ->
282
producer.onLatencyMeasured(latency -> latencyHist.recordValue(latency))
283
);
284
285
// Periodic analysis with synchronization
286
scheduledExecutor.scheduleAtFixedRate(() -> {
287
synchronized (latencyHist) {
288
analyzeLatencyDistribution(latencyHist);
289
}
290
}, 0, 10, TimeUnit.SECONDS);
291
```
292
293
### Use ConcurrentHistogram When:
294
- Need full concurrent feature support
295
- Auto-resizing is required
296
- Value shifting operations are needed
297
- Can coordinate reading/analysis phases
298
299
```java
300
// Auto-resizing concurrent histogram for varying workloads
301
ConcurrentHistogram responseTime = new ConcurrentHistogram(3);
302
303
// Multiple threads recording different ranges
304
recordingThreads.forEach(thread ->
305
thread.onResponse(time -> responseTime.recordValue(time))
306
);
307
308
// Coordinated analysis
309
WriterReaderPhaser phaser = responseTime.getWriterReaderPhaser();
310
phaser.readerLock();
311
try {
312
generateReport(responseTime);
313
} finally {
314
phaser.readerUnlock();
315
}
316
```
317
318
### Use SynchronizedHistogram When:
319
- Simple thread safety is preferred over performance
320
- Frequent queries from multiple threads
321
- Blocking behavior is acceptable
322
- Easy integration with existing synchronized code
323
324
```java
325
// Simple thread-safe histogram for mixed read/write workloads
326
SynchronizedHistogram requestSize = new SynchronizedHistogram(3);
327
328
// Recording thread
329
requestHandler.onRequest(req -> requestSize.recordValue(req.getSize()));
330
331
// Monitoring threads can query safely
332
monitoringThreads.forEach(monitor ->
333
monitor.scheduleReporting(() -> {
334
// No synchronization needed
335
double avgSize = requestSize.getMean();
336
long p95Size = requestSize.getValueAtPercentile(95.0);
337
reportMetrics(avgSize, p95Size);
338
})
339
);
340
```
341
342
## Performance Considerations
343
344
### AtomicHistogram Performance
345
- **Best** for recording-heavy workloads
346
- Cache line contention possible with many threads
347
- Consider using multiple histograms and merging periodically
348
349
### ConcurrentHistogram Performance
350
- Writer-reader phaser adds coordination overhead
351
- Excellent scaling for mixed read/write workloads
352
- Auto-resize may cause temporary pauses
353
354
### SynchronizedHistogram Performance
355
- Simplest implementation but may limit scalability
356
- Good for lower-frequency operations
357
- Contention increases with thread count
358
359
## Memory Models and Visibility
360
361
All concurrent variants provide proper memory visibility guarantees:
362
- Recorded values are visible across threads
363
- Statistical queries reflect all recorded values up to the query point
364
- No additional memory barriers needed for basic usage