Internal kernel of ReentrantReadWriteLock - private field 'sync' of type AbstractQueuedSynchronizer (AQS).
AQS - one of greatest 'synchronizers' of Doug Lea. It contains single-linked list of contended Threads. Every Thread marked is it 'exclusive' or 'shared'.
Read for more info "The java.util.concurrent Synchronizer Framework".
You can periodically examine blocked thread queue (100 times in second) and collect stats.
import java.lang.reflect.Field;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class App {
static final AtomicInteger freeTime = new AtomicInteger(0);
static final AtomicInteger fullTime = new AtomicInteger(0);
static final AtomicInteger readersLength = new AtomicInteger(0);
static final AtomicInteger writersLength = new AtomicInteger(0);
public static float contended() {
return fullTime.get() / (fullTime.get() + freeTime.get());
}
public static float uncontended() {
return freeTime.get() / (fullTime.get() + freeTime.get());
}
public static float meanReadersQueue() {
return readersLength.get() / fullTime.get();
}
public static float meanWritersQueue() {
return writersLength.get() / fullTime.get();
}
public static void main(String[] args) throws Exception {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
AbstractQueuedSynchronizer sync =
useReflection(lock, "sync", AbstractQueuedSynchronizer.class);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
int queueLength = sync.getQueueLength();
if (queueLength == 0) {
freeTime.incrementAndGet();
} else {
fullTime.incrementAndGet();
int readersCount = sync.getSharedQueuedThreads().size();
readersLength.addAndGet(readersCount);
int writersCount = sync.getExclusiveQueuedThreads().size();
writersLength.addAndGet(writersCount);
}
}, 0, 10, TimeUnit.MILLISECONDS);
}
private static <T> T useReflection(Object from, String name, Class<T> to) throws Exception {
Field f = from.getClass().getDeclaredField(name);
f.setAccessible(true);
return (T) f.get(from);
}
}