2013-07-24 8 views
32

Sto solo avendo un momento difficile per capire il concetto dietro la messa in wait() nella classe Object. Per questo problema, considerare se wait() e notifyAll() sono nella classe Thread.Concetto dietro l'inserimento dei metodi wait(), notify() nella classe Object

class Reader extends Thread { 
    Calculator c; 
    public Reader(Calculator calc) { 
     c = calc; 
    } 

    public void run() { 
     synchronized(c) {        //line 9 
     try { 
      System.out.println("Waiting for calculation..."); 
      c.wait(); 
     } catch (InterruptedException e) {} 
      System.out.println("Total is: " + c.total); 
     } 
    } 

    public static void main(String [] args) { 
     Calculator calculator = new Calculator(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     calculator.start(); 
    } 
} 

class Calculator extends Thread { 
    int total; 
    public void run() { 
     synchronized(this) {      //Line 31 
      for(int i=0;i<100;i++) { 
       total += i; 
      } 
      notifyAll(); 
     } 
    } 
} 

La mia domanda è che differenza avrebbe potuto fare? Nella riga 9 stiamo acquisendo il blocco sull'oggetto c e quindi l'esecuzione dell'attesa che soddisfa la condizione di attesa che dobbiamo acquisire il blocco sull'oggetto prima di utilizzare wait e quindi è il caso di notifyAll che abbiamo acquisito il blocco sull'oggetto di Calculator alla riga 31 .

+4

È difficile capire cosa stai chiedendo ... – assylias

+0

ti sto chiedendo se abbiamo messo in attesa e notificato nella classe Thread quindi penso che questo codice avrebbe funzionato. – Sunny

+1

'.wait()' e '.notify {, Tutti}()' si trovano su 'Object', non' Thread'. Questo è ciò che consente l'implementazione di molte primitive di blocco nella JVM ('Semaforo',' CountDownLatch', ecc.) – fge

risposta

79

Sto solo avendo difficoltà a capire concetto alla base di mettere wait() nella classe di oggetti Per questo amor domande considerare come se wait() e notifyAll() sono in classe thread

nel linguaggio Java, è wait() su una particolare istanza di un Object - un monitor assegnato a tale oggetto per essere precisi. Se si desidera inviare un segnale a un thread che è in attesa su quella specifica istanza dell'oggetto, si chiama notify() su tale oggetto. Se si desidera inviare un segnale a tutti i thread che sono in attesa su tale istanza dell'oggetto, si utilizza notifyAll() su tale oggetto.

Se wait() e notify() erano sullo Thread, invece, ogni thread avrebbe dovuto conoscere lo stato di ogni altro thread. In che modo thread1 sa che thread2 stava aspettando l'accesso a una particolare risorsa? Se thread1 avesse bisogno di chiamare thread2.notify(), avrebbe dovuto in qualche modo scoprire che thread2 era in attesa. Dovrebbe esserci un meccanismo per i thread per registrare le risorse o le azioni di cui hanno bisogno in modo che altri possano segnalarli quando le cose sono pronte o disponibili.

In Java, l'oggetto stesso è l'entità condivisa tra thread che consente loro di comunicare tra loro. I thread non hanno conoscenze specifiche l'uno dell'altro e possono essere eseguiti in modo asincrono. Corrono e si bloccano, aspettano e notificano l'oggetto a cui desiderano accedere. Non hanno conoscenza di altri thread e non hanno bisogno di conoscere il loro stato.Non hanno bisogno di sapere che è thread2 che sta aspettando la risorsa - si limitano a notificare sulla risorsa e chiunque sia in attesa (se qualcuno) verrà avvisato.

In Java, utilizziamo quindi oggetti di blocco come sincronizzazione, mutex e punti di comunicazione tra i thread. Ci sincronizziamo su un oggetto lock per ottenere l'accesso mutex a un importante blocco di codice e per sincronizzare la memoria. Aspettiamo un oggetto se stiamo aspettando che alcune condizioni cambino: alcune risorse diventano disponibili. Informiamo su un oggetto se vogliamo risvegliare i fili dormiente.

// locks should be final objects so the object instance we are synchronizing on, 
// never changes 
private final Object lock = new Object(); 
... 
// ensure that the thread has a mutex lock on some key code 
synchronized (lock) { 
    ... 
    // i need to wait for other threads to finish with some resource 
    // this releases the lock and waits on the associated monitor 
    lock.wait(); 
    ... 
    // i need to signal another thread that some state has changed and they can 
    // awake and continue to run 
    lock.notify(); 
} 

Ci può essere un numero qualsiasi di oggetti di blocco nel programma, ognuno dei quali blocca una particolare risorsa o segmento di codice. Potresti avere 100 oggetti di blocco e solo 4 thread. Poiché i thread eseguono le varie parti del programma, ottengono l'accesso esclusivo a uno degli oggetti di blocco. Di nuovo, non devono conoscere lo stato di esecuzione degli altri thread.

Ciò consente di aumentare o diminuire il numero di thread in esecuzione nel software quanto si desidera. Si scopre che i 4 thread stanno bloccando troppo le risorse esterne, quindi è possibile aumentare il numero. Spingendo troppo il server danneggiato, riduci il numero di thread in esecuzione. Gli oggetti lock assicurano mutex e la comunicazione tra i thread indipendentemente dal numero di thread in esecuzione.

+3

Wow autoesplicativo, ma come hai indicato desidero entrare nel dettaglio perché chiamiamo .Wait() dal blocco sincronizzato come in stato di attesa rilascia il blocco per gli altri che rende le risorse accessibili ad altri thread. – Sunny

+0

Questa è solo una parte della specifica @Sunny. È necessario avere il blocco per inviare un 'notify()', quindi 'wait()' deve prima sbloccarlo. – Gray

2

risposta alla tua prima domanda è come ogni oggetto in Java ha una sola lock(monitor) e wait(),notify(),notifyAll() sono utilizzati per monitorare la condivisione di questo è il motivo per cui essi sono parte di Object classe piuttosto che Thread classe.

+0

true, ma se l'attesa era parte della classe Thread, allora penso anche che avrebbero potuto condividere il blocco – Sunny

-1

il metodo wait() rilascia il blocco sull'oggetto specificato e attende quando è in grado di recuperare il blocco.

il notify(), notifyAll() controllerà se ci sono thread in attesa di ottenere il blocco di un oggetto e se possibile lo darà a loro.

Il motivo per cui i blocchi fanno parte degli oggetti è perché le risorse (RAM) sono definite da Object e non da Thread.

Il metodo più semplice per capire questo è che i thread possono condividere oggetti (nell'esempio è la calcolatrice condivisa da tutti i thread), ma gli oggetti non possono condividere attributi (come le primitive, anche i riferimenti stessi agli oggetti non sono condivisi, puntano solo nella stessa posizione). Quindi, in orde per assicurarsi che un solo thread modificherà un oggetto, il sistema di chiusura sincronizzata viene utilizzato

+0

La tua risposta è confusione di blocchi e condizioni. Sono diversi. aspetta rilascia il lucchetto e attende le condizioni. Notifica rilascia una discussione (o thread) in attesa della condizione ma non rilascia il blocco. – Gray

4

Le altre risposte a questa domanda mancano tutte del punto chiave in Java, c'è un mutex associato a ogni oggetto. (Suppongo che tu sappia cos'è un mutex o un "blocco".) Questo è non il caso nella maggior parte dei linguaggi di programmazione che hanno il concetto di "serrature". Ad esempio, in Ruby, devi creare esplicitamente tutti gli oggetti Mutex di cui hai bisogno.

Penso di sapere perché i creatori di Java hanno fatto questa scelta (anche se, a mio parere, è stato un errore). Il motivo ha a che fare con l'inclusione della parola chiave synchronized. Credo che i creatori di Java (ingenuamente) pensassero che includendo i metodi synchronized nella lingua, sarebbe diventato facile per le persone scrivere codice multithread corretto: incapsulare tutto lo stato condiviso negli oggetti, dichiarare i metodi che accedono a tale stato come synchronized, e il gioco è fatto! Ma non ha funzionato in quel modo ...

In ogni modo, dal momento che ogni classe può avere synchronized metodi, ci deve essere un mutex per ogni oggetto, che i metodi synchronized possibile bloccare e sbloccare.

wait e notify entrambi si basano su mutex. Forse hai già capito perché questo è il caso ... se non posso aggiungere ulteriori spiegazioni, ma per ora, diciamo solo che entrambi i metodi devono lavorare su un mutex. Ogni oggetto Java ha un mutex, quindi ha senso che wait e notify possano essere chiamati su qualsiasi oggetto Java. Ciò significa che devono essere dichiarati come metodi di Object.

Un'altra opzione sarebbe stata quella di inserire metodi statici su Thread o qualcosa del genere, che avrebbe richiesto qualsiasi Object come argomento. Sarebbe stato molto meno confuso con i nuovi programmatori Java. Ma non l'hanno fatto in quel modo. È troppo tardi per cambiare una di queste decisioni; peccato!

+0

La mia risposta parla specificamente di un monitor per oggetto. Inoltre, in Java è anche possibile utilizzare 'ReentrantLock' o altri meccanismi di blocco integrati nel JDK, se lo si desidera. – Gray

+2

OK, annotato, +1 per includere quel punto. È vero che le versioni successive di JRE includono oggetti di blocco espliciti, ma fin dal primo giorno, i mutex impliciti sono stati lì, ed è per questo che 'wait' e' notify' sono stati resi metodi su 'Object'. Se oggetti di blocco espliciti, o meglio ancora, gli oggetti di coda delle condizioni erano inclusi nel JRE originale, quindi 'wait' e' notify' sarebbero stati associati con loro. –

0

Questi metodi funzionano sui blocchi e sui blocchi sono associati a Oggetti e non a Thread. Quindi, è nella classe Object.

I metodi wait(), notify() e notifyAll() non sono solo metodi, sono utilità di sincronizzazione e vengono utilizzati nel meccanismo di comunicazione tra i thread in Java.

Per una spiegazione più dettagliata, si prega di visitare: http://parameshk.blogspot.in/2013/11/why-wait-notify-and-notifyall-methods.html

0

Questo è solo il mio 2 centesimi su questa domanda ... non so se questo è vero nella sua interezza.

Ogni oggetto ha un monitor e un waitset -> set di thread (questo è probabilmente più a livello di sistema operativo). Ciò significa che il monitor e il waitset possono essere visti come membri privati ​​di un oggetto. Avere metodi wait() e notify() nella classe Thread significherebbe dare l'accesso pubblico al waitset o usare i metodi get-set per modificare il waitset. Non vorresti farlo perché è una cattiva progettazione.

Ora che l'Oggetto conosce il thread/i in attesa del suo monitor, dovrebbe essere il lavoro dell'Oggetto per andare a risvegliare quei thread che lo aspettano piuttosto che una classe Object of thread che va e risveglia ognuno di essi (che sarebbe possibile solo se l'oggetto classe thread ha accesso al waitset). Tuttavia, non è compito di un determinato thread andare a risvegliare ciascuno dei thread in attesa. (Questo è esattamente quello che sarebbe successo se tutti questi metodi fossero all'interno della classe Thread). Il suo compito è solo quello di rilasciare il blocco e andare avanti con il proprio compito. Un thread funziona in modo indipendente e non ha bisogno di sapere se altri thread sono in attesa del monitoraggio degli oggetti (è un dettaglio non necessario per l'oggetto classe thread). Se ha iniziato a risvegliare ciascun thread da solo, si sta allontanando dalla sua funzionalità di base e cioè per svolgere il proprio compito. Quando pensi a una scena in cui potrebbero esserci migliaia di thread ... puoi assumere l'impatto che una performance può creare. Quindi, dato che Object Class sa chi sta aspettando su di esso, può svolgere il lavoro risvegliando i thread in attesa e il thread che ha inviato notify() può eseguire con la sua ulteriore elaborazione.

Per dare un'analogia (forse non quella giusta ma non riesco a pensare ad altro). Quando abbiamo un'interruzione di corrente, chiamiamo un rappresentante del cliente di tale azienda perché conosce le persone giuste da contattare per risolverlo. Come utente non è permesso sapere chi sono gli ingegneri dietro di esso e, anche se lo sai, non è possibile chiamare ciascuno di loro e dirgli quali sono i tuoi problemi (non è un tuo dovere. l'interruzione e il lavoro del CR è di andare a notificare (risvegliare) gli ingegneri giusti per questo).

Fammi sapere se questo suona bene ... (Ho la capacità di confondere a volte con le mie parole).

0

le operazioni di attesa e notifica funzionano sul blocco implicito e il blocco implicito è un'operazione che rende possibile la comunicazione tra thread. E tutti gli oggetti hanno la loro copia di oggetto implicito. quindi, aspettare e notificare dove è implicita la serratura è una buona decisione.

In alternativa attesa e notifica potrebbero essere vissute anche nella classe Thread. piuttosto che aspettare() potremmo dover chiamare Thread.getCurrentThread(). wait(), lo stesso con notify. Per le operazioni di attesa e notifica ci sono due parametri obbligatori, uno è il thread che aspetterà o notificherà l'altro è il blocco implicito dell'oggetto. entrambi sono disponibili in Object e anche in Thread. Il metodo wait() nella classe Thread avrebbe fatto lo stesso che sta facendo nella classe Object, passa il thread corrente allo stato di attesa attende sul blocco che aveva acquisito per ultimo.

Quindi sì, penso che l'attesa e la notifica avrebbero potuto essere presenti anche nella classe Thread, ma è più come una decisione progettuale per mantenerla in classe oggetto.

22

Per capire meglio perché il metodo wait() e notify() appartiene alla classe Object, fornirò un esempio di vita reale: Supponiamo che una stazione di servizio disponga di un servizio igienico singolo, la chiave per il quale è tenuta al servizio scrivania. Il bagno è una risorsa condivisa per il passaggio degli automobilisti.Per utilizzare questa risorsa condivisa, il potenziale utente deve acquisire una chiave per il lucchetto sul water. L'utente si reca al banco assistenza e acquisisce la chiave, apre la porta, la blocca dall'interno e utilizza le strutture.

Nel frattempo, se un secondo potenziale utente arriva alla stazione di servizio, trova il bagno bloccato e quindi non disponibile a lui. Va al banco assistenza ma la chiave non è lì perché è nelle mani dell'utente corrente. Quando l'utente corrente termina, sblocca la porta e restituisce la chiave al service desk. Non si preoccupa di aspettare i clienti. Il service desk fornisce la chiave per il cliente in attesa. Se più di un potenziale utente si presenta mentre il gabinetto è bloccato, devono formare una coda in attesa della chiave del lucchetto. Ogni thread non ha idea di chi sia nella toilette.

Ovviamente nell'applicare questa analogia a Java, 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 usando la parola chiave sincronizzata, 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.

Infine, la risposta alla tua domanda: la serratura potrebbe essere l'oggetto chiave o il banco assistenza. Nessuno dei quali è un Thread.

Tuttavia, questi sono gli oggetti che attualmente decidono se il WC è bloccato o aperto. Questi sono gli oggetti che sono in grado di notificare che il bagno è aperto ("notifica") o chiedere alle persone di aspettare quando è bloccato attendere.

+0

Spiegazione chiara ..... –

+0

Le persone nel mondo sono come i fili sulla loro strada usano le risorse condivise come le sedie di attesa nella stazione ferroviaria, la stazione di rifornimento di benzina ecc. Tutte queste persone non sanno chi sta aspettando per queste che acquistano e rilasciare risorse. Le risorse annunciano che sono gratuite e disponibili, non le persone, questo è il motivo per cui la classe oggetto ha metodi wait() e notify(). – malatesh

1

In termini semplici, i motivi sono i seguenti.

  1. Object dispone di monitor.
  2. Più thread possono accedere a uno Object. Solo un thread può contenere il monitor oggetti alla volta per i metodi/blocchi synchronized.
  3. wait(), notify() and notifyAll() metodo essendo in Object classe consente a tutti i processi creati su quel object di comunicare con altri
  4. Chiusura (usando synchronized or Lock API) e comunicazione (wait() and notify()) sono due concetti diversi.

Se Thread classe contiene wait(), notify() and notifyAll() metodi, allora creerà sotto problemi:

  1. Thread problema di comunicazione
  2. Synchronization in oggetto non sarà possibile. Se ogni filo avrà monitor, non avremo alcun modo per ottenere una sincronizzazione
  3. Inconsistency in stato dell'oggetto

riferiscono a questo article per maggiori dettagli.

+0

I thread non vengono creati "su un oggetto". Più thread non esistono per un oggetto. – Gray

+0

frase riformulata. –

-1

Il metodo di attesa e notifica sempre chiamato su oggetto, quindi se può essere oggetto Thread o oggetto semplice (che non estende la classe Thread) Dato l'esempio, si cancellano tutti i dubbi.

Ho chiamato wait e notify sulla classe ObjB e questa è la classe Thread, quindi possiamo dire che wait e notify sono richiamati su qualsiasi oggetto.

public class ThreadA { 
    public static void main(String[] args){ 
     ObjB b = new ObjB(); 
     Threadc c = new Threadc(b); 
     ThreadD d = new ThreadD(b); 
     d.setPriority(5); 
     c.setPriority(1); 
     d.start(); 
     c.start(); 
    } 
} 

class ObjB { 
    int total; 
    int count(){ 
     for(int i=0; i<100 ; i++){ 
      total += i; 
     } 
     return total; 
    }} 


class Threadc extends Thread{ 
    ObjB b; 
    Threadc(ObjB objB){ 
     b= objB; 
    } 
    int total; 
    @Override 
    public void run(){ 
     System.out.print("Thread C run method"); 
     synchronized(b){ 
      total = b.count(); 
      System.out.print("Thread C notified called "); 
      b.notify(); 
     } 
    } 
} 

class ThreadD extends Thread{ 
    ObjB b; 
    ThreadD(ObjB objB){ 
     b= objB; 
    } 
    int total; 
    @Override 
    public void run(){ 
     System.out.print("Thread D run method"); 
     synchronized(b){ 
      System.out.println("Waiting for b to complete..."); 
      try { 
       b.wait(); 
       System.out.print("Thread C B value is" + b.total); 
       } 
       catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
     } 
    } 
} 
0

aspettare - aspettare metodo indica il thread corrente di rinunciare monitor e andare a dormire.

notify - Riattiva un singolo thread che è in attesa sul monitor di questo oggetto.

Quindi i metodi wait() e notify() funzionano a livello di monitor, thread che al momento sta trattenendo il monitor è invitato a rinunciare a quel monitor tramite il metodo wait() e attraverso i metodi notify notify (o notifyAll) che sono in attesa sul monitor dell'oggetto vengono notificati che i thread possono svegliarsi.

Importante punto da notare qui è che il monitor è assegnato a un oggetto non a un thread particolare. Questo è uno dei motivi per cui questi metodi si trovano nella classe Object. Per reiterare i thread, attendere sul monitor di un oggetto (blocco) e notify() viene anche chiamato su un oggetto per riattivare un thread in attesa sul monitor dell'oggetto.