Come possono essere chiamati i metodi wait () e notify () sugli oggetti che non sono thread?

Come possono essere chiamati i metodi wait() e notify() su Oggetti che non sono Thread? Non ha davvero senso, vero?

Sicuramente, deve avere senso, tuttavia, poiché i due metodi sono disponibili per tutti gli oggetti Java. Qualcuno può fornire una spiegazione? Ho difficoltà a capire come comunicare tra thread usando wait() e notify() .

I blocchi sono diversi dai thread. Il blocco si trova sulla struttura dati protetta. I thread sono le cose che accedono alla struttura dei dati. I blocchi si trovano sull’object della struttura dati per impedire ai thread di accedere alla struttura dati in modo non sicuro.

Qualsiasi object può essere utilizzato come blocco intrinseco (significato usato insieme a synchronized ). In questo modo puoi proteggere l’accesso a qualsiasi object aggiungendo il modificatore sincronizzato ai metodi che accedono ai dati condivisi. (Non che sia una buona idea, perché consente a qualsiasi thread che può accedere all’object di acquisire il proprio blocco, anche se non sta chiamando alcun metodo su di esso, è meglio mantenere il blocco come membro privato della struttura dati bloccata, in modo che l’accesso ad esso sia limitato).

wait e notify vengono richiamati su oggetti che vengono utilizzati come blocchi. Il blocco è un punto di comunicazione condiviso:

  • Quando un thread che ha un lock chiama notifyAll su di esso, gli altri thread in attesa su quello stesso lock vengono notificati. Quando un thread che ha una chiamata di blocco notify su di esso, uno dei thread in attesa su quello stesso blocco viene notificato.

  • Quando un thread che ha una chiamata di blocco lo wait , il thread rilascia il blocco e passa in sospeso fino a quando a) riceve una notifica, o b) si sveglia semplicemente arbitrariamente (il “wakeful spurio”); il thread in attesa rimane bloccato nella chiamata per attendere finché non si ritriggers a causa di uno di questi 2 motivi, quindi il thread deve riacquistare il blocco prima che possa uscire dal metodo wait.

Vedi il tutorial Oracle sui blocchi protetti , la class Drop è la struttura dati condivisa, i thread che utilizzano i runnables Producer e Consumer stanno accedendo ad esso. Il blocco dell’object Drop controlla in che modo i thread accedono ai dati dell’object Drop.

I thread vengono utilizzati come blocchi nell’implementazione JVM, si consiglia agli sviluppatori di applicazioni di evitare l’uso di thread come blocchi. Ad esempio, la documentazione per Thread.join dice:

Questa implementazione utilizza un ciclo di chiamate this.wait condizionate su this.isive. Quando un thread termina, viene invocato il metodo this.notifyAll. Si consiglia alle applicazioni di non utilizzare le istanze wait, notify o notifyAll on Thread.

È ansible utilizzare wait() e notify() per sincronizzare la logica. Come esempio

 synchronized (lock) { lock.wait(); // Will block until lock.notify() is called on another thread. } // Somewhere else... ... synchronized (lock) { lock.notify(); // Will wake up lock.wait() } 

con lock come membro della class Object lock = new Object();

È ansible interrompere il thread per il tempo che si desidera utilizzando il metodo sleep() class della funzione Thread sleep() .

 public class Main { //some code here //Thre thread will sleep for 5sec. Thread.sleep(5000); } 

Se vuoi fermare alcuni oggetti devi chiamare questo metodo all’interno di blocchi syncronized .

 public class Main { //some code public void waitObject(Object object) throws InterruptedException { synchronized(object) { object.wait(); } } public void notifyObject(Object object) throws InterruptedException { synchronized(object) { object.notify(); } } 

}

PS Sono sory se sbaglio capisco la tua domanda (l’inglese non è il mio nativo)

Pensa usando un esempio di vita reale, un bagno . Quando vuoi usare il bagno nel tuo ufficio, hai due opzioni per assicurarti che nessun altro arrivi in ​​bagno una volta che lo usi.

  1. Blocca la porta del bagno, così tutti sapranno che è usata da qualcun altro quando cercano di aprire la porta
  2. Vai da ciascuna persona in ufficio, chiudi le sedie (o il tavolo o qualsiasi altra cosa), vai in bagno.

Quale opzione sceglieresti?

Sì, è lo stesso nel Javaland !.

Quindi, nella storia di cui sopra,

  • Washroom = Oggetto che vuoi bloccare (che solo tu devi usare)
  • I tuoi colleghi dello staff = altri thread che vuoi tenere fuori

Quindi, proprio come nella vita reale, quando hai degli affari privati, blocchi quell’object. E quando hai finito con quell’object, lasci andare la serratura!

(Sì, sì !, questa è una descrizione molto semplice su ciò che accade. Naturalmente il concetto reale è leggermente diverso da questo, ma questo è un punto di partenza)

In Java all Object implementa questi due metodi, ovviamente se non ci sono monitor questi due metodi sono inutili.

Quando inserisci del codice all’interno del blocco sincronizzato:

  sychronized(lock){...} 

un thread che vuole eseguire qualsiasi cosa all’interno di questo blocco acquisisce prima un blocco su un object e solo un thread alla volta può eseguire il codice bloccato sullo stesso object. Qualsiasi object può essere usato come un blocco, ma si deve fare attenzione a scegliere l’object rilevante per l’ambito. Ad esempio quando hai più thread che aggiungono qualcosa all’account e tutti hanno un codice responsabile per questo all’interno di un blocco come:

 sychronized(this){...} 

quindi nessuna sincronizzazione ha luogo perché sono tutti bloccati su oggetti diversi. Invece si dovrebbe usare un object account come blocco. Ora considera che questi thread hanno anche un metodo per prelevare da un account. In questo caso può verificarsi una situazione in cui un thread che desidera ritirare qualcosa incontra un account vuoto. Dovrebbe aspettare finché non ci sono dei soldi e rilasciare il blocco su altri thread per evitare un deadlock. Ecco a cosa servono i metodi wait e notify. In questo esempio, un thread che incontra un account vuoto rilascia il blocco e attende il segnale da qualche thread che effettua il deposito:

 while(balance < amountToWithdraw){ lock.wait(); } 

Quando un altro thread deposita del denaro, segnala altri thread in attesa sullo stesso blocco. (Ovviamente, il codice responsabile per effettuare depositi e prelievi deve essere sincronizzato sullo stesso blocco affinché funzioni e prevenga il danneggiamento dei dati).

 balance += amountToDeposit; lock.signallAll; 

Come vedi i metodi attendere e notificare ha senso solo all'interno di blocchi o metodi sincronizzati.

  1. L’attesa e la notifica non sono solo normali metodi o utilità di sincronizzazione, ma sono anche meccanismi di comunicazione tra due thread in Java. E la class Object è il posto giusto per renderli disponibili per ogni object se questo meccanismo non è disponibile tramite qualsiasi parola chiave java come sincronizzata. Ricordare sincronizzati e attendere la notifica sono due aree diverse e non confondere che siano uguali o correlati. Sincronizzato è quello di fornire l’esclusione reciproca e garantire la sicurezza del thread di class Java come condizioni di gara mentre attendere e notificare sono meccanismo di comunicazione tra due thread.
  2. I blocchi sono resi disponibili in base all’Oggetto, che è un’altra ragione per cui l’attesa e la notifica vengono dichiarate nella class Object piuttosto che nella class Thread.
  3. In Java per entrare nella sezione critica del codice, Threads richiede il lock e aspettano il lock, non sanno quali thread detengono lock, ma sanno solo che il lock è in attesa di qualche thread e dovrebbero aspettare il lock invece di sapere quale thread è all’interno del blocco sincronizzato e chiede loro di rilasciare il blocco. questa analogia si adatta con attesa e notifica di essere sulla class object piuttosto che su thread in Java.

Analogia: un thread Java è un utente e il bagno è un blocco di codice che il thread desidera eseguire. Java fornisce un modo per bloccare il codice per un thread che lo sta attualmente eseguendo utilizzando il keywokd sincrono, e facendo in modo che altri thread che lo desiderano possano attendere fino al termine del primo thread. Questi altri thread sono posizionati nello stato di attesa. Java non è FAIR come la stazione di servizio perché non c’è coda per i thread in attesa. Qualunque thread in attesa potrebbe ottenere il monitor successivo, indipendentemente dall’ordine in cui lo hanno richiesto. L’unica garanzia è che tutti i thread inizieranno a utilizzare il codice monitorato prima o poi.

fonte

Se guardi il seguente codice produttore e consumatore:
sharedQueue Object agisce sulla comunicazione tra thread tra producer and consumer thread del producer and consumer .

 import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; public class ProducerConsumerSolution { public static void main(String args[]) { Vector sharedQueue = new Vector(); int size = 4; Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer"); Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer"); prodThread.start(); consThread.start(); } } class Producer implements Runnable { private final Vector sharedQueue; private final int SIZE; public Producer(Vector sharedQueue, int size) { this.sharedQueue = sharedQueue; this.SIZE = size; } @Override public void run() { for (int i = 0; i < 7; i++) { System.out.println("Produced: " + i); try { produce(i); } catch (InterruptedException ex) { Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex); } } } private void produce(int i) throws InterruptedException { // wait if queue is full while (sharedQueue.size() == SIZE) { synchronized (sharedQueue) { System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: " + sharedQueue.size()); sharedQueue.wait(); } } // producing element and notify consumers synchronized (sharedQueue) { sharedQueue.add(i); sharedQueue.notifyAll(); } } } class Consumer implements Runnable { private final Vector sharedQueue; private final int SIZE; public Consumer(Vector sharedQueue, int size) { this.sharedQueue = sharedQueue; this.SIZE = size; } @Override public void run() { while (true) { try { System.out.println("Consumed: " + consume()); Thread.sleep(50); } catch (InterruptedException ex) { Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex); } } } private int consume() throws InterruptedException { //wait if queue is empty while (sharedQueue.isEmpty()) { synchronized (sharedQueue) { System.out.println("Queue is empty " + Thread.currentThread().getName() + " is waiting , size: " + sharedQueue.size()); sharedQueue.wait(); } } //Otherwise consume element and notify waiting producer synchronized (sharedQueue) { sharedQueue.notifyAll(); return (Integer) sharedQueue.remove(0); } } } 

fonte

“Questo metodo dovrebbe essere chiamato solo da un thread che è il proprietario del monitor di questo object.” Quindi penso che devi assicurarti che ci sia un thread che è il monitor sull’object.