昨天我注意到一件很奇怪的事情。似乎两个线程正在同时进入锁定同一对象的两个同步块。
包含相关代码的类 ( 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
关键字:
同步语句代表执行线程获取互斥锁(第 17.1 节),执行块,然后释放锁。当执行线程拥有锁时,没有其他线程可以获取锁。[ Java 语言规范,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)