2010-07-13 34 views
7

我注意到昨天的东西很奇怪。看来两个线程同时进入同一个对象锁定的两个同步块。同步部分不会阻止!

含有相关代码的类(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) 
    ... 

(我改变了类和方法名称为简单的情况下,所以不要被愚蠢的名字混淆。)

看来,THR EAD HTTP-8080-136和HTTP-8080-111双双获得上myLock锁。它与对象地址是相同的对象:0x00007fd8a6b8c790。 Java运行规范是怎么了​​关键字:

synchronized语句获得代表执行线程的互斥锁(§17.1),执行块,然后释放锁。虽然执行线程拥有锁,没有其他线程可以获取锁。 [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) 

回答

4

我问过热点-dev邮件列表上同样的问题,得到了克里斯托弗·菲利普斯非常GOOT答案:


嗨爱德华

我认为它的线程转储误导。

如果你真的认为2是同时在锁中,你应该得到一个gcore(外部一致)。

你看到“等待监视实体”的状态实际上是MONITOR_WAIT能代表实际的收购热锁之前下面的代码: (另见osThread.hpp OSThreadContendState)从名为: SRC /共享/ VM /运行/同步器。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. 

克里斯

1

线程转储是如何进行的?如果线程没有暂停,那么在一个线程和下一个线程之间转换锁的拥有权可能会发生变化。

+0

产生于退出信号发送到进程。我不知道Sun VM在线程转储期间的行为如何。但我认为这个过程已经停止了。否则,你会得到一个不一致的线程转储。 – 2010-07-13 09:19:54

+0

我知道对于IBM JVM来说,这并不一定是真实的,但对SUN来说却不太确定,但绝对要记住一些东西。 – 2010-07-13 09:25:29

+0

本网站声称所有线程都处于暂停状态:http://expertodev.wordpress.com/2009/05/30/how-to-take-java-thread-dump/ – 2010-07-13 09:44:34

0

我认为相关的信息是:“等待监视实体”,这是两个线程相同。由于两个线程(在线程转储中)都被标记为deamon线程,所以我猜还必须有一个主线程同时运行。主线程是否有可能阻止其他两个线程的当前显示器所有者?

+0

不,主线程无效。即使主线程持有锁,规范也不会区分主线程和deamon线程。只有一个线程被允许拥有一个锁。 – 2010-07-13 09:21:35

+0

我同意,只有一个线程被允许获得锁定并进入关键部分(根据规范)。正如线程转储中所述,两个deamon线程都等待锁定。你确定,目前没有其他线程持有锁吗? – Javaguru 2010-07-13 09:28:08

+0

两个线程http-8080-136和http-8080-111都持有该锁。线程转储中还有另外44个线程正在“等待”该锁。 – 2010-07-13 09:40:57

0

他们还没有获得锁定,否则您会在堆栈跟踪中看到xsMethodA或xsMethodB。

+0

那么为什么线程http-8080-111和http-8080-146之间有区别? – 2010-07-13 09:32:54

+0

并且:ThreadDumpAnalyzer将这两个线程(http-8080-136,http-8080-111)都列为“被锁定”。 – 2010-07-13 09:34:14

+0

我的意思是说,“同步不阻止”的标题是错误的。可能是你的意思是“没有人持锁的同步块块”? – 2010-07-13 10:24:11