昨天我注意到了一些非常奇怪的事情。似乎有两个线程同时进入两个同步代码块,锁定了相同的对象。
包含相关代码的类(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)