测量特定监视器上的线程争用

Measure thread contention on particular monitor

我正在考虑从 synchronized 切换到 ReadWriteLock。在做之前,我想看看是否值得。

ThreadMXBeanThreadInfo 提供有关总体线程阻塞计数和时间的信息。这些块可能是由多个监视器引起的。有没有一种方法可以测量给定特定监视器对象的块统计信息?

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

您需要编写一个本地代理来处理一对事件:

两个事件都接受 jthreadjobject 参数,这些参数对应于获取监视器的线程和监视器对象本身。


Here is 争用分析器代理的示例代码。

jmc, yourkit and jprofiler 都提供锁争用分析。