昨天我注意到一些非常奇怪的事情。看起来两个线程正在同时进入锁定在同一个对象上的两个同步块(synchronized block)。
包含相关代码的类 (MyClass
) 看起来与此类似:
private static int[] myLock = new int[0];
protected static int methodA(final long handle, final byte[] sort) {
synchronized (myLock) {
return xsMethodA(handle, sort);
}
}
protected static int methodB(final long handle) {
synchronized (myLock) {
return xsMethodB(handle);
}
}
我为运行上述类的应用程序创建了一个线程转储,当我看到这个时非常惊讶:
"http-8080-136" daemon prio=10 tid=0x00000000447df000 nid=0x70ed waiting for monitor entry [0x00007fd862aea000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.MyClass.methodA(MyClass.java:750)
- locked <0x00007fd8a6b8c790> (a [I)
at com.SomeOtherClass.otherMethod(SomeOtherClass.java:226)
...
"http-8080-111" daemon prio=10 tid=0x00007fd87d1a0000 nid=0x70c8 waiting for monitor entry [0x00007fd86e15f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.MyClass.methodB(MyClass.java:991)
- locked <0x00007fd8a6b8c790> (a [I)
at com.SomeOtherClass.yetAnotherMethod(SomeOtherClass.java:3231)
...
(为了简单起见,我更改了类和方法的名称,所以不要被愚蠢的名称弄糊涂了。)
似乎线程 http-8080-136 和 http-8080-111 都获得了 myLock
上的锁。它是同一个对象,因为对象地址相同:0x00007fd8a6b8c790
。 Java 运行时规范对 synchronized
关键字是这样说的:
A synchronized statement acquires a mutual-exclusion lock (§17.1) on behalf of the executing thread, executes a block, then releases the lock. While the executing thread owns the lock, no other thread may acquire the lock. [The Java Language Specification, 14.19]
这怎么可能呢?
线程转储中还有另外 44 个线程在“等待”锁。这是线程等待时的样子:
"http-8080-146" daemon prio=10 tid=0x00007fd786dab000 nid=0x184b waiting for monitor entry [0x00007fd8393b6000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.MyClass.methodC(MyClass.java:750)
- waiting to lock <0x00007fd8a6b8c790> (a [I)
at com.SomeOtherClass.yetAnoterMethod2(SomeOtherClass.java:226)
最佳答案
我在 hotspot-dev 邮件列表上问过同样的问题,并从 Christopher Phillips 那里得到了非常好的回答:
嗨爱德华
我认为是线程转储具有误导性。
如果您真的认为这 2 个同时处于锁定状态,您可能应该获得一个 gcore(外部一致)。
你看到的“waiting for monitor entry”的状态实际上是MONITOR_WAIT,在实际获取热锁之前可以表示如下代码: (另见 osThread.hpp 中的 OSThreadContendState)调用自: src/share/vm/runtime/synchronizer.cpp
3413 OSThreadContendState osts(Self->osthread());
3414 ThreadBlockInVM tbivm(jt);
3415
3416 Self->set_current_pending_monitor(this);
3417
3418 // TODO-FIXME: change the following for(;;) loop to straight-line code.
3419 for (;;) {
3420 jt->set_suspend_equivalent();
3421 // cleared by handle_special_suspend_equivalent_condition()
3422 // or java_suspend_self()
3423
3424 EnterI (THREAD) ;
3425
3426 if (!ExitSuspendEquivalent(jt)) break ;
3427
3428 //
3429 // We have acquired the contended monitor, but while we were
3430 // waiting another thread suspended us. We don't want to enter
3431 // the monitor while suspended because that would surprise the
3432 // thread that suspended us.
克里斯
关于java - 同步部分不阻塞!,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3235599/