对于 Java 对象,有没有办法告诉哪个线程(或 null)当前拥有它的监视器?或者至少是一种判断当前线程是否拥有它的方法?
4 回答
我自己找到了一些答案。测试当前线程是否持有监视器,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;
}
有一些注意事项:
- 它有点慢(在我的情况下约为 ½ 毫秒,并且可能随着线程数线性增加)。
- 它需要 Java 1.6,并且需要一个虚拟机
ThreadMXBean.isObjectMonitorUsageSupported()
,因此它的可移植性较差。 - 它需要“监视器”安全权限,因此可能无法从沙盒小程序中运行。
- 如果需要,将线程 ID 转换为 Thread 对象有点不重要,因为我想您必须使用 Thread.enumerate 然后循环找出哪个具有 ID,但这具有理论意义竞争条件,因为当您调用 enumerate 时,该线程可能不再存在,或者可能出现了具有相同 ID 的新线程。
但是,如果您只想测试当前线程,那就Thread.holdsLock
太好了!否则, 的实现java.util.concurrent.locks.Lock
可能会提供比普通 Java 监视器更多的信息和灵活性(感谢@user1252434)。
java 类监视器是 JVM 内部的,你不能真正使用它。
如果您知道对象已锁定,则可以尝试再次获取监视器-如果可以获取,则表示您正在从线程中锁定对象(因为 java 锁是递归的-您可以从同一个锁定两次线)。问题是您无法尝试同步。
您可以使用unsafe对象来执行此操作。unsafe 有一种tryMonintorEnter()
方法可以做到这一点。看到不安全。
Unsafe 可能实际上可以帮助您获得持有监视器的线程,但我不知道该怎么做......
除了使用synchronized
,您可能想看看ReentrantLock,尤其是它的方法getOwner()
和isHeldByCurrentThread()
. 但是,使用它需要更多的纪律,因为你明确地必须使用unlock()
它,最好是在一个finally
块中。
在Java 1.6中,您可以使用反射来获取此信息。
ThreadMXBean tBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = tBean .getThreadInfo(bean.getAllThreadIds(), true, true);