2010-07-13 86 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