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

4

我正在考虑从synchronized切换到ReadWriteLock。在此之前,我想检查是否值得这样做。

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


非常感谢,我只熟悉jconsole和visualvm,它们不提供锁竞争信息。 jmc看起来就是我想要的。您能在下面发布您的答案吗? - Konstantin Milyutin
3个回答

4

是的,使用JVMTI是可能的。

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

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


这是一个争用分析代理的示例代码,点击此处查看。

3

1

ReentrantReadWriteLock 的内部内核 - 类型为 AbstractQueuedSynchronizer (AQS) 的私有字段 'sync'。 AQS 是 Doug Lea 最伟大的“同步器”之一。它包含争用线程的单向链表。每个线程都标记为“独占”或“共享”。 了解更多信息 "java.util.concurrent 同步器框架"

您可以定期检查阻塞的线程队列(每秒 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);
    }
}

1
它如何回答我的问题? - Konstantin Milyutin
您可以定期检查阻塞线程队列(每秒100次),并收集统计信息。 - Ivan Golovach
在我的问题中,我解释了我不想在确定它是否值得之前切换。 - Konstantin Milyutin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接