测量特定监视器上的线程争用
Measure thread contention on particular monitor
我正在考虑从 synchronized
切换到 ReadWriteLock
。在做之前,我想看看是否值得。
ThreadMXBean
和 ThreadInfo
提供有关总体线程阻塞计数和时间的信息。这些块可能是由多个监视器引起的。有没有一种方法可以测量给定特定监视器对象的块统计信息?
ReentrantReadWriteLock 的内部内核 - AbstractQueuedSynchronizer (AQS) 类型的私有字段 'sync'。
AQS - Doug Lea 最伟大的 'synchronizers' 之一。它包含竞争线程的单链表。每个标记的线程都是 'exclusive' 或 'shared'。
阅读以获取更多信息 "The java.util.concurrent Synchronizer Framework"。
您可以定期检查阻塞的线程队列(每秒 100 次)并收集统计信息。
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);
}
}
是的,可以使用 JVMTI。
您需要编写一个本地代理来处理一对事件:
- MonitorContendedEnter - 当线程即将进入已被另一个线程获取的同步块时触发;
- MonitorContendedEntered - 当线程在等待后成功获取监视器时触发。
两个事件都接受 jthread
和 jobject
参数,这些参数对应于获取监视器的线程和监视器对象本身。
Here is 争用分析器代理的示例代码。
我正在考虑从 synchronized
切换到 ReadWriteLock
。在做之前,我想看看是否值得。
ThreadMXBean
和 ThreadInfo
提供有关总体线程阻塞计数和时间的信息。这些块可能是由多个监视器引起的。有没有一种方法可以测量给定特定监视器对象的块统计信息?
ReentrantReadWriteLock 的内部内核 - AbstractQueuedSynchronizer (AQS) 类型的私有字段 'sync'。 AQS - Doug Lea 最伟大的 'synchronizers' 之一。它包含竞争线程的单链表。每个标记的线程都是 'exclusive' 或 'shared'。 阅读以获取更多信息 "The java.util.concurrent Synchronizer Framework"。
您可以定期检查阻塞的线程队列(每秒 100 次)并收集统计信息。
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);
}
}
是的,可以使用 JVMTI。
您需要编写一个本地代理来处理一对事件:
- MonitorContendedEnter - 当线程即将进入已被另一个线程获取的同步块时触发;
- MonitorContendedEntered - 当线程在等待后成功获取监视器时触发。
两个事件都接受 jthread
和 jobject
参数,这些参数对应于获取监视器的线程和监视器对象本身。
Here is 争用分析器代理的示例代码。