Perché tutti gli oggetti Java hanno wait () e notification () e questo provoca un calo delle prestazioni?

StackOverflow https://stackoverflow.com/questions/1648551

  •  22-07-2019
  •  | 
  •  

Domanda

Ogni Java Object ha i metodi wait() e notify() (e varianti aggiuntive). Non li ho mai usati e sospetto che molti altri non l'abbia fatto. Perché questi sono così fondamentali che ogni oggetto deve averli e c'è un impatto sulle prestazioni nel possederli (presumibilmente un certo stato è memorizzato in essi)?

MODIFICA per enfatizzare la domanda. Se ho un List<Double> con 100.000 elementi, ogni Double ha questi metodi in quanto è esteso da List. Ma sembra improbabile che tutti questi debbano conoscere i thread che gestiscono <=>.

MODIFICA risposte eccellenti e utili. @Jon ha un ottimo post sul blog che ha cristallizzato i miei sentimenti viscerali. Sono anche completamente d'accordo con @Bob_Cross che dovresti mostrare un problema di prestazioni prima di preoccupartene. (Anche come l'ennesima legge delle lingue di successo se fosse stato un successo da esibizione, Sun o qualcuno l'avrebbero risolto).

È stato utile?

Soluzione

Bene, significa che ogni oggetto deve potenzialmente avere un monitor associato ad esso. Lo stesso monitor viene utilizzato per synchronized. Se sei d'accordo con la decisione di essere in grado di sincronizzare su qualsiasi oggetto, allora wait() e notify() non aggiungere altro stato per oggetto. La JVM può allocare pigramente il monitor effettivo (lo so .NET.) Ma deve esserci dello spazio di archiviazione disponibile per dire quale monitor è associato all'oggetto. Certo, è possibile che si tratti di una quantità molto piccola (ad es. 3 byte) che in realtà non risparmierebbe alcun tipo di memoria a causa del riempimento del resto dell'oggetto in testa - dovresti guardare come dire ogni singola JVM ha gestito la memoria per dire di sicuro.

Nota che il solo fatto di avere metodi extra non influisce sulle prestazioni (a parte un po 'a causa dell'evidente presenza del codice da qualche parte ). Non è come ogni oggetto o anche ogni tipo ha la propria copia del codice per <=> e <=>. A seconda di come funzionano le vtabili, ogni tipo può finire con una voce vtable aggiuntiva per ciascun metodo ereditato, ma che è ancora solo per tipo, non per oggetto. Fondamentalmente questo si perderà nel rumore rispetto alla maggior parte della memoria che è per gli oggetti reali stessi.

Personalmente, ritengo che sia .NET che Java abbiano commesso un errore associando un monitor ad ogni oggetto - preferirei invece avere oggetti di sincronizzazione esplicita. Ho scritto un po 'di più su questo in un post sul blog sulla riprogettazione di java.lang.Object / System.Object .

Altri suggerimenti

  

Perché sono così fondamentali   ogni oggetto deve averli ed è   c'è un colpo di prestazione nel averli   (presumibilmente un certo stato è memorizzato in   loro)?

tl; dr: sono metodi di sicurezza dei thread e hanno piccoli costi in relazione al loro valore.

Le realtà fondamentali che questi metodi support sono:

  1. Java è sempre multi-thread. Esempio: controlla l'elenco dei thread utilizzati da un processo usando jconsole o jvisualvm qualche volta.
  2. La correttezza è più importante di " performance. " Quando stavo valutando i progetti (molti anni fa), dovevo spiegare & Quot; arrivare alla risposta sbagliata veramente veloce è ancora sbagliato. & Quot;

Fondamentalmente, questi metodi forniscono alcuni degli hook per gestire i monitor per oggetto utilizzati nella sincronizzazione. In particolare, se ho synchronized(objectWithMonitor) in un metodo particolare, posso usare objectWithMonitor.wait() per produrre quel monitor (ad esempio, se ho bisogno di un altro metodo per completare un calcolo prima di poter procedere). In tal caso, ciò consentirà un altro metodo che è stato bloccato in attesa che quel monitor procedesse.

D'altra parte, posso usare objectWithMonitor.notifyAll() per far sapere ai Thread che stanno aspettando il monitor che presto lascerò il monitor. In realtà non possono procedere fino a quando non lascerò il blocco sincronizzato.

Per quanto riguarda esempi specifici (ad esempio, Liste lunghe di doppi) in cui potresti preoccuparti che si verifichi un colpo di prestazioni o di memoria sul meccanismo di monitoraggio, ecco alcuni punti che dovresti probabilmente considerare:

  1. Innanzitutto, dimostralo. Se pensi che vi sia un impatto notevole da un meccanismo Java di base come la correttezza multi-thread, c'è un'eccellente possibilità che l'intuizione sia falsa. Misura prima l'impatto. Se è serio e sai che non dovrai mai sincronizzarti su un singolo Double, prendi in considerazione l'utilizzo dei doppi.
  2. Se non sei sicuro che tu, il tuo collaboratore, un futuro programmatore di manutenzione (che potrebbe essere te stesso un anno dopo), ecc., non avrai mai mai bisogno di una granularità fine di accesso diretto ai tuoi dati, c'è un'eccellente possibilità che togliere questi monitor renderebbe il tuo codice meno flessibile e gestibile.

Follow-up in risposta alla domanda su oggetti monitor per oggetto rispetto a oggetti espliciti:

Risposta breve: @JonSkeet: sì, rimuovere i monitor creerebbe problemi: creerebbe attrito. Mantenere questi monitor in Object ci ricorda che questo è sempre un sistema multithread.

I monitor degli oggetti integrati non sono sofisticati ma sono: facili da spiegare; lavorare in modo prevedibile; e sono chiari nel loro scopo. synchronized(this) è una chiara dichiarazione di intenti. Se forziamo i programmatori principianti a utilizzare esclusivamente il pacchetto di concorrenza, introduciamo attrito. Cosa c'è in quel pacchetto? Che cos'è un semaforo? Fork-join?

Un programmatore principiante può usare i monitor degli oggetti per scrivere un codice decente per il controller della vista modello. synchronized, wait e notifyAll possono essere utilizzati per implementare la sicurezza dei thread ingenua (nel senso di prestazioni semplici, accessibili ma forse non all'avanguardia). L'esempio canonico sarebbe uno di questi doppi (posti dall'OP) che può avere un thread impostato un valore mentre il thread AWT ottiene il valore per inserirlo in un JLabel. In tal caso, non vi è alcuna buona ragione per creare un oggetto aggiuntivo esplicito solo per avere un monitor esterno.

A un livello leggermente superiore di complessità, questi stessi metodi sono utili come metodo di monitoraggio esterno. Nell'esempio sopra, l'ho fatto esplicitamente (vedi frammenti objectWithMonitor sopra). Ancora una volta, questi metodi sono davvero utili per mettere insieme una sicurezza dei thread relativamente semplice.

Se yo vorresti essere ancora più sofisticato, penso che dovresti seriamente pensare di leggere Java Concurrency In Practice (se non l'hai già fatto). I blocchi di lettura e scrittura sono molto potenti senza aggiungere troppa complessità aggiuntiva.

Punchline: utilizzando i metodi di sincronizzazione di base, puoi sfruttare gran parte delle prestazioni abilitate dai moderni processori multi-core con thread-safety e senza sovraccarico.

A tutti gli oggetti in Java sono associati dei monitor. Le primitive di sincronizzazione sono utili in quasi tutto il codice multi-thread, ed è semanticamente molto piacevole da sincronizzare sugli oggetti a cui si sta accedendo piuttosto che su & Quot; Monitor & Quot; oggetti.

Java può allocare i monitor associati agli oggetti secondo necessità - come fa .NET - e in ogni caso l'overhead effettivo per l'allocazione (ma non l'utilizzo) del blocco sarebbe piuttosto ridotto.

In breve: è davvero conveniente memorizzare oggetti con i loro bit di supporto per la sicurezza dei thread e l'impatto sulle prestazioni è molto ridotto.

Questi metodi sono in giro per implementare la comunicazione tra thread.

Controlla questo articolo sull'argomento .

Regole per questi metodi, tratte da quell'articolo:

  • wait () dice al thread chiamante di rinunciare al monitor e di andare a dormire fino a quando un altro   il thread entra nello stesso monitor e chiama notification ().
  • notification () attiva il primo thread che ha chiamato wait () sullo stesso oggetto.
  • notifyAll () attiva tutti i thread che hanno chiamato wait () sullo stesso oggetto. Il   il thread con priorità più alta verrà eseguito per primo.

Spero che questo aiuti ...

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