2009-10-30 11 views
19

Ogni Java Object ha i metodi wait() e notify() (e varianti aggiuntive). Non li ho mai usati e sospetto che molti altri non l'abbiano fatto. Perché questi sono così fondamentali che ogni oggetto deve averli e c'è un colpo di performance nel loro (presumibilmente qualche stato è memorizzato in loro)?Perché tutti gli oggetti Java hanno wait() e notify() e ciò provoca un calo di prestazioni?

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

EDIT risposte eccellenti e utili. @ Jon ha un post sul blog molto buono che ha cristallizzato i miei sentimenti istintivi. Sono anche completamente d'accordo con @Bob_Cross sul fatto che dovresti mostrare un problema di prestazioni prima di preoccupartene. (Anche come l'ennesima legge delle lingue di successo se fosse stato un successo in termini di prestazioni, allora Sun o qualcuno l'avrebbe risolto).

+0

Il tuo esempio è scarsamente scelto, poiché String è immutabile e quindi apolide che è una rarità relativa. –

+0

@ Kevin - grazie. Modificherà. –

risposta

32

Beh, vuol dire che ogni oggetto deve potenzialmente hanno un monitor ad esso associati. 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 reale (so che .NET lo fa) ma deve esserci dello spazio di archiviazione disponibile per dire quale monitor è associato all'oggetto. È possibile che si tratti di una quantità molto piccola (ad es. 3 byte) che non salverà comunque memoria a causa del riempimento del resto dell'overhead dell'oggetto. Dovresti esaminare come ogni singola JVM ha gestito la memoria per dire di sicuro.

Si noti che solo avendo in più metodi non influisce sulle prestazioni (diverso da leggermente a causa del codice ovvio essere presente in qualche luogo ). Non è come ogni oggetto o anche ogni tipo ha una propria copia del codice per wait() e notify(). A seconda di come funzionano i vtables, ogni tipo può terminare con una voce extra vtable per ogni metodo ereditato, ma è sempre solo in base al tipo, non in base all'oggetto. Questo è fondamentalmente andando a perdersi nel rumore rispetto alla maggior parte dello storage che è per gli oggetti stessi.

Personalmente, sento che entrambi.NET e Java hanno commesso un errore associando un monitor a ogni oggetto: preferirei invece disporre di oggetti di sincronizzazione espliciti. Ho scritto un po 'di più su questo in un blog post about redesigning java.lang.Object/System.Object.

+1

@Jon grazie - questo è stato anche il mio punto di vista. L'hotspot –

+1

non può nemmeno assegnare un monitor per l'oggetto, a meno che non cambi le mani. (IBM VM è lo stesso anche). In tal caso, gonfia l'intestazione dell'oggetto e alloca il mutex. Sync in generale non è molto economico (http://java.sun.com/performance/reference/whitepapers/6_performance.html#2.1.1). Anche wait/notify sono definitivi (e per lo più nativi), quindi non prendono nemmeno parte a nessun VTable. – bestsss

+0

@bestsss: cosa intendi esattamente con "gonfiando l'intestazione dell'oggetto"? L'intestazione dell'oggetto di dimensioni variabili, quindi? Yikes. In .NET, credo che il monitor sia assegnato solo su richiesta (e di nuovo, se non c'è mai nessuna contesa, di solito non è necessario allocarne uno), ma non penso che le dimensioni dell'intestazione dell'oggetto cambino. –

1

Questi metodi sono disponibili per implementare la comunicazione tra thread.

Controllare this article on the subject.

Regole per quei metodi, presi da tale articolo:

  • wait() racconta il thread chiamante per dare il monitor e andare a dormire fino a qualche altro filo immette lo stesso monitor e chiamate notifica() .
  • notify() riattiva il primo thread che ha chiamato wait() sullo stesso oggetto.
  • notifyAll() riattiva tutti i thread che hanno chiamato wait() sullo stesso oggetto. Il thread con priorità più alta verrà eseguito per primo.

Spero che questo aiuti ...

+3

Non fanno inter-* process * comunicazione - fanno inter- * thread * coordinamento. –

+0

thx per correggere. – KB22

+0

Lì. Corretto. –

2

Tutti gli oggetti in Java hanno dei monitor associati. Le primitive di sincronizzazione sono utili in quasi tutto il codice multi-threaded e sono semanticamente molto piacevoli da sincronizzare sugli oggetti a cui si accede piuttosto che su oggetti separati "Monitor".

Java può allocare i monitor associati agli oggetti come necessario - come fa .NET - e in ogni caso l'overhead effettivo per allocare semplicemente (ma non utilizzare) il blocco sarebbe piuttosto piccolo.

In breve: è molto conveniente archiviare oggetti con i relativi bit di supporto per la sicurezza dei fili e l'impatto sulle prestazioni è molto ridotto.

+0

Non penso che sia davvero conveniente: la pratica migliore consiglia di incapsulare i blocchi non esponendoli all'esterno della classe a meno che non sia necessario (ad esempio non bloccare "questo" o "Foo.class"). Sarebbe più elegante * e * leggermente più efficiente in termini di spazio di archiviazione non avere un monitor potenzialmente associato a ciascuna classe. –

+0

Ehhh ... se esponi regolarmente i campi di una classe hai problemi oltre al blocco dell'efficienza. Sono d'accordo sul fatto che è possibile salvare un po 'di memoria dichiarando esplicitamente gli oggetti di blocco, ma dovrebbe essere nell'ordine di sizeof (void *) o sizeof (int) per oggetto (assumendo l'inizializzazione pigra); se sei __that__ interessato probabilmente non dovresti collegare in un runtime grande quanto la JVM. –

12

Perché sono questi così fondamentale che ogni oggetto deve avere loro e è c'è un calo di prestazioni in averli (presumibilmente uno stato viene memorizzato nella loro)?

tl; dr: Sono metodi di sicurezza del filo e hanno costi ridotti rispetto al loro valore.

Le realtà fondamentali che these methods supporto sono che:

  1. Java è sempre multi-threaded. Esempio: controlla l'elenco dei Thread usati da un processo usando jconsole o jvisualvm un po 'di tempo.
  2. La correttezza è più importante della "prestazione". Quando stavo valutando i progetti (molti anni fa), dovevo spiegare "arrivare alla risposta sbagliata veramente veloce è ancora sbagliato."

Fondamentalmente, questi metodi forniscono alcuni dei ganci per gestire i monitor per oggetto utilizzati nella sincronizzazione. Specificamente, se ho synchronized(objectWithMonitor) in un metodo particolare, posso usare objectWithMonitor.wait() per produrre quel monitor (ad es., 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 il monitor proceda.

D'altra parte, posso usare objectWithMonitor.notifyAll() per consentire ai Thread che stanno aspettando il monitor di sapere che ho intenzione di abbandonare presto il monitor. In realtà, non possono procedere finché non esco dal blocco sincronizzato.

Rispetto ad esempi specifici (ad esempio, lunghe liste di Doubles) dove si potrebbe preoccupare che ci sia una performance o la memoria colpire sul meccanismo di monitoraggio, qui ci sono alcuni punti che si dovrebbe probabilmente prendere in considerazione:

  1. First , Provalo. Se pensi che ci sia un forte impatto da un meccanismo Java di base come la correttezza multi-thread, c'è un'eccellente possibilità che il tuo intuito sia falso. Misura prima l'impatto. Se è una cosa seria e tu rispondi allo allo che non avrai mai bisogno di sincronizzare su un singolo Double, considera invece l'uso del doppio.
  2. Se non sei sicuro che tu, tuo collaboratore, un futuro programmatore di manutenzione (chi potrebbe essere te stesso un anno dopo), ecc., mai e poi mai , sempre, avremo bisogno di una buona granularità dell'accesso ai tuoi dati, ci sono ottime possibilità che togliere questi monitor renderebbe il tuo codice meno flessibile e manutenibile.

Follow-up in risposta alla domanda su per-Object vs. oggetti espliciti del monitor:

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

I monitor degli oggetti incorporati 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 costringiamo i programmatori principianti a utilizzare esclusivamente il pacchetto di concorrenza, introduciamo l'attrito. Cosa c'è in quel pacchetto? Cos'è un semaforo? Fork-join?

Un codificatore inesperto può utilizzare i monitor Oggetto per scrivere codice decente del modello di visualizzazione del modello. synchronized, wait e notifyAll possono essere utilizzati per implementare l'ingenuo (nel senso di prestazioni di sicurezza semplici, accessibili ma forse non all'avanguardia). L'esempio canonico sarebbe uno di questi Doubles (postato dall'OP) che può avere un Thread impostato su un valore mentre il thread AWT ottiene il valore per metterlo su una JLabel. In tal caso, non vi è alcun motivo valido per creare un oggetto aggiuntivo esplicito solo per avere un monitor esterno.

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

Se desideri essere ancora più sofisticato, penso che dovresti seriamente pensare a leggere Java Concurrency In Practice (se non lo 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, è possibile sfruttare gran parte delle prestazioni abilitate dai moderni processori multi-core con thread-safety e senza molto overhead.

+0

+1 per "Primo, provalo." :) –

+1

@ Arion Digulla, grazie - personalmente, penso che dovrebbe essere la prima frase in qualsiasi argomento di prestazione. –

+1

Mentre sono d'accordo con tutti i consigli qui, non vedo alcuna connessione tra questi elementi di funzionalità fondamentalmente importanti, e sono fondamentali * per ogni oggetto *. In altre parole, una di queste risposte non funzionerebbe se si dovesse usare un oggetto separato per il monitor? –