13

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

4

4 回答 4

12

我自己找到了一些答案。测试当前线程是否持有监视器,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(),因此它的可移植性较差。
  3. 它需要“监视器”安全权限,因此可能无法从沙盒小程序中运行。
  4. 如果需要,将线程 ID 转换为 Thread 对象有点不重要,因为我想您必须使用 Thread.enumerate 然后循环找出哪个具有 ID,但这具有理论意义竞争条件,因为当您调用 enumerate 时,该线程可能不再存在,或者可能出现了具有相同 ID 的新线程。

但是,如果您只想测试当前线程,那就Thread.holdsLock太好了!否则, 的实现java.util.concurrent.locks.Lock可能会提供比普通 Java 监视器更多的信息和灵活性(感谢@user1252434)。

于 2013-06-20T08:24:24.090 回答
2

java 类监视器是 JVM 内部的,你不能真正使用它。

如果您知道对象已锁定,则可以尝试再次获取监视器-如果可以获取,则表示您正在从线程中锁定对象(因为 java 锁是递归的-您可以从同一个锁定两次线)。问题是您无法尝试同步。

您可以使用unsafe对象来执行此操作。unsafe 有一种tryMonintorEnter()方法可以做到这一点。看到不安全

Unsafe 可能实际上可以帮助您获得持有监视器的线程,但我不知道该怎么做......

于 2013-06-20T07:04:41.603 回答
2

除了使用synchronized,您可能想看看ReentrantLock,尤其是它的方法getOwner()isHeldByCurrentThread(). 但是,使用它需要更多的纪律,因为你明确地必须使用unlock()它,最好是在一个finally块中。

于 2013-06-20T07:30:43.060 回答
1

Java 1.6中,您可以使用反射来获取此信息。

ThreadMXBean tBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = tBean .getThreadInfo(bean.getAllThreadIds(), true, true);
于 2013-06-20T07:13:41.977 回答