2009-04-14 2 views
11

In primo luogo, here's a sample:Come funziona sincronizzata lavoro in Java

public class Deadlock { 
    static class Friend { 
     private final String name; 
     public Friend(String name) { 
      this.name = name; 
     } 
     public String getName() { 
      return this.name; 
     } 
     public synchronized void bow(Friend bower) { 
      System.out.format("%s: %s has bowed to me!%n", 
        this.name, bower.getName()); 
      bower.bowBack(this); 
     } 
     public synchronized void bowBack(Friend bower) { 
      System.out.format("%s: %s has bowed back to me!%n", 
        this.name, bower.getName()); 
     } 
    } 

    public static void main(String[] args) { 
     final Friend alphonse = new Friend("Alphonse"); 
     final Friend gaston = new Friend("Gaston"); 
     new Thread(new Runnable() { 
      public void run() { alphonse.bow(gaston); } 
     }).start(); 
     new Thread(new Runnable() { 
      public void run() { gaston.bow(alphonse); } 
     }).start(); 
    } 
} 

Quello che non capisco è come si verifica il blocco. La funzione principale avvia due thread che ognuno inizia i propri archi.

Che cosa fa esattamente il blocco "sincronizzato"? La stessa funzione in esecuzione per lo stesso oggetto (come inizialmente pensavo)? La stessa funzione per tutti gli oggetti della stessa classe? Tutte le funzioni sincronizzate per lo stesso oggetto? Tutte le funzioni sincronizzate per tutti gli oggetti della stessa classe?

Aiutatemi qui.

risposta

22

In Java, ogni Object offre la possibilità per un thread di synchronize o di bloccarsi su di esso. Quando un metodo è sincronizzato, il metodo utilizza l'istanza dell'oggetto come blocco. Nell'esempio, i metodi bow e bowBack sono entrambi synchronized ed entrambi sono nella stessa classe Friend. Ciò significa che qualsiasi Thread che esegue questi metodi si sincronizzerà su un'istanza Friend come blocco.

Una sequenza di eventi che causano un deadlock è:

  1. Il primo Thread started chiamate alphonse.bow(gaston), che è synchronized sull'oggetto alphonseFriend. Ciò significa che il thread deve acquisire il blocco da questo oggetto.
  2. Il secondo thread ha iniziato le chiamate gaston.bow(alphonse), che è synchronized sull'oggetto gastonFriend. Ciò significa che il thread deve acquisire il blocco da questo oggetto.
  3. Il primo thread avviato ora chiama bowback e attende il rilascio del blocco gaston.
  4. Il secondo thread avviato chiama ora bowback e attende il rilascio del blocco alphonse.

Per mostrare la sequenza di eventi in modo molto più dettagliato:

  1. main() comincia a eseguire nel principale Therad (lo chiamano discussione # 1), creando due Friend istanze. Fin qui tutto bene.
  2. Il thread principale inizia il suo primo nuovo thread (chiamalo thread # 2) con il codice new Thread(new Runnable() { .... Il thread n. 2 chiama alphonse.bow(gaston), ovvero synchronized sull'oggetto alphonseFriend. Il thread n. 2 acquisisce quindi il "lock" per l'oggetto alphonse e immette il metodo bow.
  3. Qui si verifica un intervallo di tempo e il thread originale ha la possibilità di eseguire ulteriori elaborazioni.
  4. Il thread principale avvia un secondo nuovo thread (chiamalo thread # 3), proprio come il primo. Il thread n. 3 chiama gaston.bow(alphonse), che è sincronizzato sull'oggetto gastonFriend. Poiché nessuno ha ancora acquisito il "blocco" per l'istanza dell'oggetto gaston, il thread n. 3 acquisisce questo blocco ed entra nel metodo bow.
  5. Un intervallo di tempo si verifica qui e il thread n. 2 ha la possibilità di eseguire più elaborazioni.
  6. Il thread n. 2 ora chiama bower.bowBack(this); con bower come riferimento all'istanza di gaston. Questo è l'equivalente logico di una chiamata di gaston.bowBack(alphonse). Pertanto, questo metodo è synchronized sull'istanza gaston. Il blocco per questo oggetto è già stato acquisito ed è trattenuto da un altro Thread (Thread # 3). Pertanto, il thread n. 2 deve attendere il rilascio del blocco gaston. Il thread è messo in uno stato di attesa, consentendo al thread n. 3 di eseguire ulteriormente.
  7. La discussione n. 3 ora chiama bowback, che in questo caso è logicamente uguale alla chiamata alphonse.bowBack(gaston). Per fare ciò, è necessario acquisire il blocco per l'istanza alphonse, ma questo blocco è trattenuto dal Thread # 2. Questo thread è ora messo in uno stato di attesa.

E ora ci si trova in una posizione in cui né Thread può eseguire. Sia il Thread # 2 che il Thread # 3 attendono il rilascio di un lock. Ma nessuno dei due lock può essere rilasciato senza che il thread faccia progressi. Ma nessuno dei due thread può fare progressi senza il rilascio di un blocco.

Quindi: deadlock!

I deadlock dipendono molto spesso da una sequenza specifica di eventi che possono rendere difficile il debug poiché possono essere difficili da riprodurre.

+2

Oh ok. Quindi il lucchetto appartiene all'intero oggetto. Non so perché ho pensato che fossero solo chiamate allo stesso metodo sincronizzato per un dato oggetto che sono bloccate. Immagino che risponda alla mia domanda. –

+0

L'oggetto non esegue sincronizzazione/blocco, ma è il thread. –

+1

In realtà, ho parlato in modo specifico del Thread acquisendo il lock, ma vedo che alcune delle mie lingue sono inesatte. Lo renderò più chiaro. – Eddie

2

Synchronized has two effects:

  • primo luogo, non è possibile che due invocazioni di metodi sincronizzati sullo stesso oggetto per interlacciare. Quando un thread sta eseguendo un metodo sincronizzato per un oggetto, tutti gli altri thread che invocano metodi sincronizzati per lo stesso blocco oggetto (sospendi l'esecuzione) finché il primo thread non viene eseguito con l'oggetto.
  • In secondo luogo, quando un metodo sincronizzato viene chiuso, stabilisce automaticamente una relazione prima-evento con qualsiasi successiva chiamata di un metodo sincronizzato per lo stesso oggetto. Ciò garantisce che le modifiche allo stato dell'oggetto siano visibili a tutti i thread.

Quindi, in breve, blocca qualsiasi invocazione di metodi sincronizzati sullo stesso oggetto.

2

Tutte le funzioni sincronizzate per lo stesso oggetto. Contrassegnare un metodo "sincronizzato" è molto simile a mettere un blocco "sincronizzato (questo) {" attorno all'intero contenuto del metodo. Il motivo per cui non dico "identico" è perché non so se il compilatore emette lo stesso bytecode o meno, ma AFAIK l'effetto di runtime definito è lo stesso.

Il deadlock è una classica inversione di blocco. Un thread blocca alphonse. Quindi (o simultaneamente su un sistema multi-core) l'altro thread blocca gaston. Questa parte richiede che la pianificazione dei thread avvenga in modo appropriato per interlacciare nei punti giusti.

Ogni thread (in qualsiasi ordine o simultaneamente) tenta di acquisire un blocco che è già trattenuto dall'altro thread e quindi ogni thread va in sospensione. Nessuno dei due si sveglierà fino a quando l'altro non rilascerà il proprio blocco, ma nessuno dei due rilascerà il proprio blocco finché non si sveglierà (o verrà terminato).

2

Il metodo sincronizzato è lo stesso che racchiude tutti coloro codice metodi in un blocco

synchronized(this) { 
    /// code here ... 
} 

.

Ad una determinata istanza di oggetto o, solo thread alla volta può eseguire qualsiasi sincronizzato (o) blocco. Ogni altro thread che tenta di piangere, fino a quando il thread che esegue quel blocco (ha il blocco sincronizzato ) esce da quel blocco (rinuncia al blocco).

Nel tuo caso, il deadlock si verifica quando Alphonse inizia a piegarsi nella filettatura 1, entrando così nel blocco sincronizzato. Il thread 1 viene quindi sostituito dal sistema, quindi il thread 2 può essere avviato e Gaston si inchina. Ma Gaston non può ancora arretrare, perché è sincronizzato su Alphonse, e il Thread 1 ha già quel blocco. Attenderà quindi che il thread 1 lasci quel blocco. Il sistema quindi rimpiazzerà il Thread 1, il quale proverà a far tornare indietro Alphonse. Tranne che non può farlo perché Thread 2 ha il blocco sincronizzato su Gaston. Entrambe le discussioni sono bloccate, aspettando che l'altro finisca di inchinarsi prima di poter inchinarsi ...