2010-07-13 14 views
7

Ho notato qualcosa di molto strano ieri. Sembra che due thread inseriscano contemporaneamente due blocchi sincronizzati che si bloccano sullo stesso oggetto.La sezione sincronizzata non blocca!

La classe (MyClass) contenente il codice in questione è simile a questo:

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); 
    } 
} 

ho creato una discarica filo della mia applicazione in esecuzione la classe superiore e sono rimasto molto sorpreso come ho visto questo:

"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 cambiato i nomi delle classi e il metodo per il caso di semplicità, in modo da non confondersi con i nomi stupidi.)

sembra che thr ead http-8080-136 e http-8080-111 hanno entrambi acquisito il blocco su myLock. È lo stesso oggetto in cui l'indirizzo dell'oggetto è lo stesso: 0x00007fd8a6b8c790. Il Java Runtime Specification dice questo circa la parola synchronized:

Una dichiarazione sincronizzato acquisisce un blocco mutua esclusione (§17.1) per conto del thread in esecuzione, esegue un blocco, quindi rilascia il blocco. Mentre il thread in esecuzione possiede il blocco, nessun altro thread può acquisire il blocco. [The Java Language Specification, 14.19]

Così come è questo possibile?

Ci sono altri 44 thread nel dump di thread "in attesa" per il blocco. Ecco come appare se un thread è in attesa:

"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) 

risposta

4

Ho chiesto la stessa domanda sul hotspot-dev mailing list e ricevuto una risposta molto goot da Christopher Phillips:


Hi Eduard

penso che sia la discarica filo che è fuorviante .

Se pensate davvero che i 2 si trovano nel blocco simultaneamente dovreste probabilmente ottenere un gcore (che è esternamente coerente).

Lo stato che si vede "in attesa per l'ingresso del monitor" è in realtà MONITOR_WAIT che può rappresentare il seguente codice prima effettiva acquisizione di una serratura a caldo: (vedi anche OSThreadContendState in osThread.hpp) chiamato da: src/share/vm/runtime/sincronizzatore.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. 

Chris

1

Come è stata eseguita la discarica del filo? Se i thread non sono stati messi in pausa, la proprietà del blocco potrebbe essere cambiata tra il dumping di un thread e il successivo.

+0

Inviando il segnale QUIT al processo. Non so come agisce Sun VM durante il dump del thread. Ma suppongo che il processo sia fermato. Altrimenti si otterrebbe un dump discontinuo del thread. –

+0

So per IBM JVM questo non è necessariamente vero, non sono sicuro di SUN, tuttavia, sicuramente qualcosa da tenere a mente. –

+0

Questo sito dichiara che tutti i thread sono in pausa: http://expertodev.wordpress.com/2009/05/30/how-to-take-java-thread-dump/ –

0

Penso che le informazioni rilevanti siano: "waiting for monitor entry", che è lo stesso per entrambi i thread. Poiché entrambi i thread (nel dump del thread) sono contrassegnati da thread di deamon, suppongo che ci sia anche un thread principale in esecuzione allo stesso tempo. È possibile che il thread principale sia il proprietario del monitor corrente che blocca gli altri due thread?

+0

No, il thread principale non fa nulla. Anche se la filettatura principale reggerebbe il blocco, la specifica non distingue tra filetti principale e filettatura del deamon. Solo un thread è autorizzato a possedere un blocco. –

+0

Sono d'accordo, solo un thread è permesso di ottenere il blocco e di entrare nella sezione critica (secondo le specifiche). Entrambi i thread dei deamon * stanno * in attesa del blocco, come indicato nel dump del thread. Sei sicuro, non ci sono altri thread che attualmente bloccano il blocco? – Javaguru

+0

I due thread http-8080-136 e http-8080-111 stanno tenendo il blocco. Vi sono altri 44 thread nel dump del thread "in attesa" per il blocco. –

0

Non hanno acquisito il blocco, altrimenti si vedrebbe xsMethodA o xsMethodB in uno stacktrace.

+0

Quindi, perché c'è una differenza tra i thread http-8080-111 e http-8080-146? –

+0

E: ThreadDumpAnalyzer elenca entrambi i thread (http-8080-136, http-8080-111) come "bloccati da". –

+0

Voglio dire che il titolo "sincronizzato non blocca" è sbagliato. Potresti voler dire "blocchi di blocchi sincronizzati quando nessuno tiene il lucchetto"? –

Problemi correlati