2015-09-25 17 views
14

Sono dubbioso che, in linguaggio Java, abbiamo bisogno di tenere il blocco, prima di attendere qualche condizione per essere soddisfatti.Perché le condizioni di blocco attendono devono tenere il blocco

per esempio, int blocco del monitor java:

synchronized(lock){ 
    System.out.println("before lock ..."); 
    lock.wait(); 
    System.out.println("after lock ..."); 
} 

oi utils cucurrent.

Lock lock = new ReentrantLock(); 
Condition cond = lock.newCondition(); 

lock.lock(); 
try{ 
    System.out.println("before condition ..."); 
    cond.await(); 
    System.out.println("after condition ..."); 
}catch(Exception e){ 
    e.printStackTrace(); 
}finally{ 
    lock.unlock(); 
} 

quindi, perché non possiamo attendere, senza tenere il lucchetto?

se questo solo a causa di Java, le altre lingue funzionano diversamente?

Spero che tu possa spiegare il motivo dopo la progettazione, ma non solo per la definizione di JAVA-SPEC.

+2

Java viene eseguito nel contesto di una macchina virtuale e viene specificato il comportamento della macchina virtuale. –

+0

quindi, questo è dovuto a SPEC? – Chinaxing

+2

Beh, certo, è a causa della specificazione. Strana domanda. – EJP

risposta

10

Immagina di avere qualcosa che un thread potrebbe dover aspettare. Forse hai una coda e un thread deve aspettare fino a quando c'è qualcosa sulla coda in modo che possa elaborarlo. La coda deve essere protetta da thread, quindi deve essere protetta da un blocco. È possibile scrivere il seguente codice:

  1. Acquisire il blocco.
  2. Verificare se la coda è vuota.
  3. Se la coda è vuota, attendere che venga inserito qualcosa sulla coda.

Oops, non funzionerà. Teniamo il blocco in coda, quindi come può un altro thread posizionare qualcosa su di esso? Proviamo ancora:

  1. Acquisire il blocco.
  2. Verificare se la coda è vuota.
  3. Se la coda è vuota, rilasciare il blocco e attendere che venga inserito qualcosa sulla coda.

Oops, ora abbiamo ancora un problema. Cosa succede se dopo aver rilasciato il blocco ma prima di attendere che qualcosa venga messo in coda, qualcosa viene messo in coda? In tal caso, aspetteremo qualcosa che è già accaduto.

Le variabili di condizione esistono per risolvere questo problema esatto. Hanno un'operazione atomica di "sblocco e attesa" che chiude questa finestra.

Quindi, l'attesa deve tenere il lucchetto perché altrimenti non ci sarebbe alcun modo per assicurarsi che non si stia aspettando qualcosa che è già accaduto. Devi tenere il lucchetto per evitare che un altro filo vada in corsa con la tua attesa.

+0

In java, condizione o oggetto. Attendere solo dopo notifica o segnale. Quindi, la tua spiegazione è giusta. Penso che se possiamo lasciare la semantica della condizione in questo modo: quando aspetti/attendi, controlla il suo bit di wakeup (che è stato impostato da notifica/segnale), se è vero, basta semplicemente aspettare/attendere (invece di aspettare). e per il thread di notifica/segnale, basta impostare il bit di attivazione della condizione quando si notifica/segnala, il che significa che la condizione è soddisfatta. se il design è così, non è necessario acquisire il lucchetto? – Chinaxing

+1

@Chinaxing No, non farlo. Considera: due thread sono bloccati sulla variabile condition. Un thread mette un oggetto in coda, segnala la variabile di condizione e riattiva un thread. Un thread mette un oggetto in coda, segnala la variabile di condizione e riattiva l'altro thread. Il primo thread di woken elabora il primo elemento in coda. Quindi, prima che il secondo thread possa essere eseguito, il primo thread di woken elabora il secondo elemento in coda. Ora il secondo thread viene eseguito, la variabile condition è stata segnalata, ma la coda è vuota. –

0

La risposta semplice è perché altrimenti si otterrà IllegalMonitorStateException che è specificato in Object.wait javadoc. Internamente, la sincronizzazione in Java utilizza il meccanismo operativo OS sottostante. Quindi non è solo Java.

+0

Sì, ma l'OP vuole sapere _why_ si ottiene l'IllegalMonitorStateException. –

1

Vedere il documento per Condition.

Una condizione è come un pool di attesa o un set di attesa di un oggetto e sostituisce l'utilizzo dei metodi di monitoraggio dell'oggetto (wait, notify e notifyAll). Le condizioni consentono a un thread di sospendere l'esecuzione ("attendere") fino a quando non viene notificato da un altro thread che alcune condizioni di stato potrebbero ora essere vere. Un'istanza Condition è intrinsecamente legata a un lock, proprio come i metodi di monitoraggio dell'oggetto richiedono il blocco dell'oggetto condiviso per l'attesa o la notifica. Quindi, prima di invocare waitit() su una condizione, il thread deve aver bloccato l'oggetto Lock che viene utilizzato per produrre la condizione. Quando viene invocato il metodo await(), viene rilasciato il blocco associato alla condizione.

1

Se il thread stava semplicemente aspettando che un segnale proceda, ci sono altri meccanismi per farlo. Presumibilmente vi è uno stato protetto dal lucchetto che il filo è in attesa di essere azionato e soddisfa alcune condizioni. Per proteggere correttamente tale stato, il thread deve avere il blocco prima e dopo l'attesa della condizione, quindi è logico richiedere l'acquisizione del blocco.

8

Bene, cosa stiamo aspettando? Stiamo aspettando che una condizione diventi realtà. Un altro thread renderà vera la condizione, quindi notificherà i thread in attesa.

Prima di entrare in attesa, è necessario verificare che la condizione sia falsa; questo controllo e l'attesa devono essere atomici, cioè sotto lo stesso blocco. Altrimenti, se entriamo nell'attesa mentre la condizione è già vera, probabilmente non ci sveglieremo mai.

quindi è necessario che il blocco è già acquisito prima di chiamare wait()

synchronized(lock) 
{ 
    if(!condition) 
     lock.wait(); 

Se wait() automaticamente e silenziosamente acquisisce blocco, un sacco di bug andrà inosservato.


Al risveglio dal wait(), dobbiamo verificare lo stato di nuovo - non c'è alcuna garanzia che la condizione deve diventare vero qui (per un sacco di ragioni - sveglia spuria; timeout, interruzione, più camerieri, varie condizioni)

synchronized(lock) 
{ 
    if(!condition) 
     lock.wait(); 
    if(!condition) // check again 
     ... 

In genere, se la condizione è ancora falsa, attenderemo nuovamente. Pertanto il modello tipico è

while(!condition) 
     lock.wait(); 

Ma ci sono anche casi in cui non vogliamo aspettare ancora.


Ci potrebbe mai essere casi di utilizzo in cui legit attesa nudo/notify senso?

synchronized(lock){ lock.wait(); } 

Sicuro; un'applicazione può essere compilata con attesa/notifica nuda, con un comportamento ben definito; si può argomentare che questo è il comportamento desiderato; e questa è la migliore implementazione per quel comportamento.

Tuttavia, questo non è il tipico schema di utilizzo e non vi è alcun motivo per giustificarlo nella progettazione dell'API.

+0

Bene, anche con una "attesa nuda", abbiamo il problema che l'attesa sarà restituita solo se un altro thread chiama 'notify' * dopo che * l'attesa è iniziata. Ma senza che entrambi i thread siano "sincronizzati" sullo stesso oggetto, non esiste nemmeno una relazione di ordinamento. Va notato esplicitamente che non solo non esiste alcuna garanzia che la condizione sia diventata realtà, non esiste inoltre alcuna garanzia che la condizione non diventi nuovamente falsa tra la notifica e il risveglio. – Holger

Problemi correlati