同步部分不会阻塞!
昨天我注意到一些非常奇怪的事情。似乎两个线程同时进入锁定同一个对象的两个同步块。
包含相关代码的类 (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]< /p>
< strong>那么这怎么可能呢?
线程转储中还有另外 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)
I've noticed something very strange yesterday. It seems that two threads are entering two synchronized blocks locking on the same object at the same time.
The class (MyClass
) containing the relevant code looks similar to this:
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);
}
}
I created a thread dump of my application running the above class and was very surprised as I saw this:
"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)
...
(I changed the class and method names for the case of simplicity, so don't get confused by the silly names.)
It seems that thread http-8080-136 and http-8080-111 have both acquired the lock on myLock
. It is the same object as the object address is the same: 0x00007fd8a6b8c790
. The Java Runtime Specification says this about the synchronized
keyword:
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]
So how is this even possible?
There are another 44 threads in the thread dump "waiting" for the lock. This is how it looks like if a thread is waiting:
"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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我在热点开发邮件列表上问了同样的问题,并收到了 Christopher Phillips 的非常好的答案:
嗨,Eduard,
我认为线程转储具有误导性。
如果您确实认为两者同时处于锁定状态,您可能应该获得一个 gcore(外部一致)。
您看到的“等待监视器条目”状态实际上是 MONITOR_WAIT ,它可以表示实际获取热锁之前的以下代码:
(另请参阅 osThread.hpp 中的 OSThreadContendState)从以下位置调用:
src/share/vm/runtime/synchronizer.cpp
克里斯
I've asked the same question on the hotspot-dev mailing list and received a very goot answer from Christopher Phillips:
Hi Eduard
I think its the thread dump that is misleading.
If you really think that the 2 are in the lock simultaneously you should probably get a gcore (which is externally consistent).
The state you see "waiting for monitor entry" is actually MONITOR_WAIT which can represent the following code before actual acquisition of a hot lock :
(also see OSThreadContendState in osThread.hpp) called from:
src/share/vm/runtime/synchronizer.cpp
Chris
线程转储是如何获取的?如果线程没有暂停,锁所有权可能会在转储一个线程和下一个线程之间发生变化。
How was the thread dump taken? If the threads were not paused the lock ownership could have changed between dumping one thread and the next.
我认为相关信息是:“等待监视器条目”,这对于两个线程都是相同的。由于两个线程(在线程转储中)都被标记为守护线程,我猜想还必须有一个主线程同时运行。主线程是否有可能是阻塞其他两个线程的当前监视器所有者?
I think the relevant information is: "waiting for monitor entry", which is the same for both threads. Since both threads (in the thread dump) are marked deamon threads, I guess there must also be a main-thread running at the same time. Is it possible that the main-thread is the current monitor owner who blocks the other two threads?
它们尚未获取锁,否则您会在堆栈跟踪中看到 xsMethodA 或 xsMethodB。
They haven't acquired lock, otherwise you would see xsMethodA or xsMethodB in a stacktrace.