昨日、非常に奇妙なことに気づきました。2 つのスレッドが、同じオブジェクトをロックしている 2 つの同期ブロックに同時に入っているようです。
関連するコードを含むクラス ( 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 Runtime Specification は、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)