Domanda

Qualcuno al lavoro ha appena chiesto il ragionamento alla base di dover aspettare un'attesa all'interno di un sincronizzato.

Onestamente non riesco a vedere il ragionamento. Capisco cosa dicono i javadocs: il thread deve essere il proprietario del monitor dell'oggetto, ma perché? Quali problemi previene? (E se è effettivamente necessario, perché il metodo wait non può ottenere il monitor stesso?)

Sto cercando un perché abbastanza approfondito o forse un riferimento a un articolo. Non sono riuscito a trovarne uno in un rapido google.

Oh, come si confronta thread.sleep?

modifica: grande serie di risposte - Vorrei davvero poterne selezionare più di una perché mi hanno aiutato tutti a capire cosa stava succedendo.

È stato utile?

Soluzione

Se l'oggetto non possiede il monitor oggetto quando chiama Object.wait (), non sarà in grado di accedere all'oggetto per impostare un listener di notifica fino al rilascio del monitor. Verrà invece trattato come un thread che tenta di accedere a un metodo su un oggetto sincronizzato.

O, per dirla in altro modo, non c'è differenza tra:

public void doStuffOnThisObject()

e il seguente metodo:

public void wait()

Entrambi i metodi saranno bloccati fino al rilascio del monitor oggetti. Questa è una funzione di Java per impedire che lo stato di un oggetto venga aggiornato da più di un thread. Ha semplicemente conseguenze indesiderate sul metodo wait ().

Presumibilmente, il metodo wait () non è sincronizzato perché ciò potrebbe creare situazioni in cui il thread ha più blocchi sull'oggetto. (Vedi Specifiche del linguaggio Java / Blocco per maggiori informazioni su questo.) Più blocchi sono un problema perché il metodo wait () annullerà un solo blocco. Se il metodo fosse sincronizzato, ciò garantirebbe che solo il blocco del metodo verrà annullato lasciando comunque annullato un potenziale blocco esterno. Ciò creerebbe una condizione di deadlock nel codice.

Per rispondere alla tua domanda su Thread.sleep (), Thread.sleep () non garantisce che tutte le condizioni che stai aspettando siano state soddisfatte. L'uso di Object.wait () e Object.notify () consente a un programmatore di implementare manualmente il blocco. I thread si sbloccheranno dopo l'invio di una notifica che è stata soddisfatta una condizione. per esempio. Una lettura dal disco è terminata e i dati possono essere elaborati dal thread. Thread.sleep () richiederebbe al programmatore di eseguire il polling se la condizione è stata soddisfatta, quindi di riaddormentarsi se non lo è.

Altri suggerimenti

Molte buone risposte già qui. Ma voglio solo menzionare qui che l'altro DEVE FARE quando si utilizza wait () è farlo in un ciclo dipendente dalla condizione che stai aspettando nel caso in cui tu stia vedendo risvegli spuri, che nella mia esperienza accadono.

Per attendere che qualche altro thread cambi una condizione in true e avvisa:

synchronized(o) {
  while(! checkCondition()) {
    o.wait();
  }
}

Ovviamente, in questi giorni, consiglierei di usare il nuovo oggetto Condition in quanto è più chiaro e ha più funzionalità (come consentire più condizioni per blocco, essere in grado di controllare la lunghezza della coda di attesa, pianificazione / interruzione più flessibile, ecc. ).

 Lock lock = new ReentrantLock();
 Condition condition = lock.newCondition();
 lock.lock();
 try {
   while (! checkCondition()) {
     condition.await();
   }
 } finally {
   lock.unlock();
 }

}

Deve possedere il monitor, poiché lo scopo di wait () è di rilasciare il monitor e consentire ad altri thread di ottenere il monitor per eseguire l'elaborazione da soli. Lo scopo di questi metodi (attendere / notificare) è coordinare l'accesso ai blocchi di codice sincronizzati tra due thread che si richiedono l'un l'altro per eseguire alcune funzionalità. Non si tratta semplicemente di assicurarsi che l'accesso a una struttura di dati sia sicuro per i thread, ma di coordinare gli eventi tra più thread.

Un esempio classico sarebbe un caso produttore / consumatore in cui un thread trasferisce i dati in una coda e un altro thread consuma i dati. Il thread che consuma richiederebbe sempre al monitor di accedere alla coda, ma rilascerebbe il monitor una volta che la coda è vuota. Il thread del produttore otterrebbe quindi l'accesso per scrivere al thread solo quando il consumatore non sta più elaborando. Avrebbe notificato il thread del consumatore una volta che ha inserito più dati nella coda, in modo da poter riguadagnare il monitor e accedere nuovamente alla coda.

Aspetta rinuncia al monitor, quindi devi averlo per arrenderlo. Anche Notifica deve avere il monitor.

Il motivo principale per cui vuoi fare questo è assicurarti di avere il monitor quando torni da wait () - in genere, stai usando il protocollo wait / notification per proteggere alcune risorse condivise e vuoi che lo faccia essere sicuri di toccarlo quando l'attesa ritorna. Lo stesso vale per notifica - di solito stai cambiando qualcosa e poi chiami avvisa () - vuoi avere il monitor, fare cambiamenti e chiamare avvisare ().

Se hai fatto una funzione come questa:

public void synchWait() {
   syncronized { wait(); }
}

Non avresti avuto il monitor quando l'attesa è tornata: potresti ottenerlo, ma potresti non ottenerlo dopo.

Ecco la mia comprensione del perché la restrizione è in realtà un requisito. Sto basando questo su un'implementazione del monitor C ++ che ho fatto qualche tempo fa combinando un mutex e una variabile di condizione.

In un sistema mutex + condition_variable = monitor , il wait call imposta la variabile di condizione in uno stato di attesa e rilascia il mutex. La variabile condition è stato condiviso, quindi deve essere bloccata per evitare condizioni di competizione tra thread che vogliono attendere e thread che vogliono avvisare. Invece di introdurre ancora un altro mutex per bloccare il suo stato, viene utilizzato il mutex esistente. In Java, il mutex è correttamente bloccato quando il thread in attesa possiede il monitor.

Per lo più l'attesa viene eseguita se esiste una condizione che indica che una coda è vuota.

If(queue is empty)
     queue.wait();

Supponiamo che la coda sia vuota. Nel caso in cui il thread corrente pre-svuota dopo aver verificato la coda, quindi se un altro thread aggiunge pochi elementi in coda, il thread corrente non lo saprà e andrà in attesa stato. È sbagliato. Quindi dovremmo avere qualcosa del genere

Synchornized(queue)
{
   if(queue is empty)
          queue.wait();
}

Ora consideriamo cosa succede se si fanno aspettare come sincronizzati. Come già accennato in uno dei commenti, rilascia solo un lucchetto. Ciò significa che se wait () fosse sincronizzato nel codice sopra, sarebbe stato rilasciato solo un blocco. Implica che il thread corrente andrà in attesa con il blocco per la coda.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top