确定哪个线程拥有一个监视器

13
有没有一种方法可以告诉我们,一个Java对象当前被哪个线程(或null)所拥有?或者至少有一种方法可以判断当前线程是否拥有它的监视器?

1
你可以执行线程转储以获取这些信息。 - fge
4个回答

13

我已经自己找到了一些答案。要测试当前线程是否持有监视器,请使用Thread.holdsLock方法!

if (!Thread.holdsLock(data)) {
    throw new RuntimeException(); // complain
}

这非常快(亚微秒级别),自1.4版以来就可用。

要测试一般情况下哪个线程(或线程ID)持有锁,可以使用 java.lang.management 类(感谢 @amicngh)。

public static long getMonitorOwner(Object obj) {
    if (Thread.holdsLock(obj)) return Thread.currentThread().getId();
    for (java.lang.management.ThreadInfo ti :
            java.lang.management.ManagementFactory.getThreadMXBean()
            .dumpAllThreads(true, false)) {
        for (java.lang.management.MonitorInfo mi : ti.getLockedMonitors()) {
            if (mi.getIdentityHashCode() == System.identityHashCode(obj)) {
                return ti.getThreadId();
            }
        }
    }
    return 0;
}

这里有一些注意事项:

  1. 它有点慢(在我的情况下大约需要 ½ 毫秒,且可能会随着线程数量的增加而线性增长)。
  2. 它要求 Java 1.6,并且需要一个支持 ThreadMXBean.isObjectMonitorUsageSupported() 的 VM,因此不太可移植。
  3. 它需要“monitor”安全权限,因此似乎无法在沙盒应用程序中使用。
  4. 如果需要将线程 ID 转换为 Thread 对象,则有点棘手,因为你可能需要使用 Thread.enumerate,然后循环查找哪个线程具有该 ID,但这存在理论上的竞争条件,因为当你调用 enumerate 时,该线程可能已经不存在了,或者可能已经出现了一个具有相同 ID 的新线程。

但是如果你只想测试当前线程,则 Thread.holdsLock 很好用!另外,实现 java.util.concurrent.locks.Lock 可能会提供比普通的 Java monitors 更多的信息和灵活性(感谢 @user1252434)。


2
Java类监视器是JVM内部的,您无法真正使用它。如果您知道对象被锁定,可以尝试再次获取监视器 - 如果可以获取,则表示您正在从线程锁定该对象(因为Java锁是递归的 - 您可以从同一线程锁定两次)。问题在于,您不能尝试同步。您可以使用不安全的对象来实现。unsafe拥有一个tryMonintorEnter()方法,可以实现这一点。请参见unsafe。unsafe可能实际上可以帮助您获得持有监视器的线程,但我不知道如何做到这一点...

2

不要使用synchronized,你可能想看一下ReentrantLock,特别是它的getOwner()isHeldByCurrentThread()方法。然而,使用它需要更多的纪律性,因为你必须明确地在finally块中使用unlock()


我之前不太熟悉RentrantLock。虽然这不是我的问题,但我认为它实际上更适合我的用例,因为我可以在C类中进行锁定并在调用之间保持锁定状态。非常感谢。 - Boann
еҰӮжһңжӮЁж„ҝж„ҸпјҢеҸҜд»Ҙдҝқз•ҷжӮЁзҡ„й”Ғе®ҡйҖ»иҫ‘гҖӮдҪҝз”ЁReentrantLockжүҖйңҖзҡ„е”ҜдёҖжӣҙж”№жҳҜд»Һsynchronized(myLock) { .. }еҲ°myLock.lock(); try { .. } finally {myLock.unlock();}гҖӮдҪҶжҳҜпјҢдёҺsynchronizedдёҚеҗҢпјҢй”ҒдёҚиғҪд»…д»…жҳҜд»»дҪ•еҜ№иұЎгҖӮ - user1252434

1
在Java 1.6中,您可以使用反射来获取此信息。
ThreadMXBean tBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = tBean .getThreadInfo(bean.getAllThreadIds(), true, true);

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