Domanda

Ho appena capito che ho bisogno di sincronizzare una quantità significativa di codice di raccolta dati in un aspetto, ma le prestazioni sono una preoccupazione reale. Se le prestazioni degradano troppo viene gettato via il mio strumento. Scriverò int e long individualmente e ai vari array, ArrayLists e mappe. Ci saranno più thread di un'applicazione che farà chiamate di funzione che verranno raccolti dal mio aspetto. Che tipo di cose che dovrebbero guardare fuori per che influire negativamente sulle prestazioni? Quali modelli di codice sono più efficienti?

In particolare ho un metodo che chiama molti altri metodi di registrazione dei dati:

void foo() {
    bar();
    woz();
    ...
}

I metodi per lo più non aggiungendo un incrementazione di campi aspetto

void bar() {
    f++; // f is a field of the aspect
    for (int i = 0; i < ary.length; i++) {
        // get some values from aspect point cut
        if (some condiction) {
            ary[i] += someValue; // ary a field of the aspect
        }
     }
 }

Nel caso la sincronizzazione foo, o al bar, Woz e altri singolarmente, o dovrei spostare tutto il codice a barre, woz, ecc in foo e basta sincronizzarlo? Devo sincronizzare su this, su un oggetto di sincronizzazione appositamente creato:

private final Object syncObject = new Object();

(vedi questo post ), o su elementi di dati individuali all'interno metodi:

ArrayList<Integer> a = new ArrayList<Integer>();

void bar() {    
    synchronize(a) {
        // synchronized code
    }
}
È stato utile?

Soluzione

La concorrenza è estremamente difficile. E 'molto facile da sbagliare, e molto difficile da ottenere. Non vorrei essere troppo terribilmente preoccupati per le prestazioni a questo punto. La mia prima preoccupazione sarebbe quella di ottenere il codice concomitante di lavorare in modo sicuro (nessun deadlock o le condizioni di gara).

Ma sul tema della performance: in caso di dubbio, di profilo. E 'difficile dire quanto sia diversa schemi di sincronizzazione influisce sulle prestazioni. E 'ancora più difficile per noi dare suggerimenti. Avremmo bisogno di vedere molto di più del vostro codice e di ottenere una comprensione molto più profonda di ciò che l'applicazione fa per dare una risposta veramente utile. Al contrario, il profiling fornisce prove concrete quanto a se un approccio è più lento rispetto ad un altro. Si può anche aiutare a identificare dove il rallentamento è.

Ci sono un sacco di grandi strumenti di profiling per Java in questi giorni. I profiler Netbeans ed Eclipse sono buone.

Inoltre, vi consiglio di stare lontano dalla sincronizzazione grezzo del tutto. Provate ad usare alcune delle classi nel java.util.concurrency pacchetto. Fanno la scrittura di codice concorrente molto più facile, e molto meno soggetto a errori.

Inoltre, vi consiglio di leggere Java Concurrency in Practice da Brian Goetz, et al. E 'molto ben scritto e copre un sacco di terra.

Altri suggerimenti

regola è di non sincronizzare il this - la maggior parte delle volte è un calo di prestazioni - tutti i metodi sono sincronizzati su un oggetto.

Si consiglia di utilizzare serrature - they'a molto bello astrazione e molte caratteristiche belle come, cercando di bloccare per un periodo di tempo, e poi rinunciare:

if(commandsLock.tryLock(100, TimeUnit.MILLISECONDS)){
     try { 
         //Do something
     }finally{
          commandsLock.unlock();
     }
}else{
     //couldnt acquire lock for 100 ms
}   

I secondo parere sull'utilizzo java.util.concurrent. Mi piacerebbe fare due levls di sincronizzazione

  • sincronizzare l'accesso di raccolta (se è necessario)
  • sincronizzare l'accesso campo

accesso Collection

Se la vostra collezione sono read-only cioè nessun elementi vengono rimossi-inseriti (ma gli elementi possono cambiare) direi che si dovrebbe usare collezioni sincronizzato (ma questo potrebbe non essere necessario ...) e non lo sincronizzare iterazioni:

Leggi solo:

for (int i = 0; i < ary.length; i++) {
    // get some values from aspect point cut
    if (some condiction) {
        ary += someValue; // ary a field of the aspect
    }
 }

e ario è esempio ottenuta tramite Collections.synchronizedList.

lettura e scrittura

synchronized(ary){
    for (int i = 0; i < ary.length; i++) {
        // get some values from aspect point cut
        if (some condiction) {
            ary += someValue; // ary a field of the aspect
        }
     }
 }

In alternativa, utilizzare alcune collezioni contemporanee (come CopyOnWriteArrayList ) che è inherentently therad sicura.

La differenza principale è che - in primo sola lettura wersion qualsiasi numero di thread può iterare su questa collezione, e in secondo solo uno alla volta può scorrere. In entrambi i casi un solo therad alla volta dovrebbe incrementare ogni settore.

Accesso al campo

Sincronizza incrementations sui campi separatamente dal iterazioni sincronizzazione.

come:

  Integer foo = ary.get(ii); 
  synchronized(foo){
      foo++;
  }

Sbarazzarsi di sincronizzazione

  1. Utilizza collezioni contemporanee (da java.util.atomic - non da `Collections.synchronizedXXX', quest'ultimo ancora bisogno di sincronizzazione su attraversamento).
  2. Usa <=> che consentono di campi atomicamente incrememt.

Qualcosa si dovrebbe guardare:

Java modello di memoria - è un discorso che dà molto bello comprensione su come sincronizzazioni e di allineamento dei dati in JAVA funziona.

upadte: dal momento che scrivere il seguito, vedo che hai aggiornato un po 'la questione. Perdona la mia ignorance-- Non ho idea di quello che un "aspetto" è-- ma dal codice di esempio che hai postato, si potrebbe anche considerare l'utilizzo di Atomics / collezioni concomitanti (ad esempio AtomicInteger, AtomicIntegerArray) o noreferrer updaters campo atomiche . Questo potrebbe significare un bel ri-factoring del codice, però. (In Java 5 su un hyperthreading Xeon dual-process, il throughput AtomicIntegerArray è significativamente meglio di una matrice sincronizzato, spiacente, non ho saggiato ripetendo la prova su più proc / versione successiva JVM yet-- notare che la performance di 'sincronizzato' è migliorata da allora)

.

In assenza di informazioni o metriche più specifiche sul particolare programma, il meglio che puoi fare è solo seguire il buon design programma . Vale la pena notare che le prestazioni e l'ottimizzazione delle serrature di sincronizzazione nella JVM ha beed una delle zone (in caso contrario, l'area) che ha ricevuto la maggior parte della ricerca e attenzione nel corso degli ultimi anni. E così nelle ultime versioni di JVM, non è poi così male.

Quindi, in generale, direi che sincronizzare minimamente senza "impazzire" . Con 'minimamente', voglio dire in modo che si tiene alla serratura per quanto meno tempo possibile, e in modo che solo le parti che hanno bisogno di utilizzare tale uso blocco specifico che blocco specifico. Ma solo se il cambiamento è facile da fare ed è facile dimostrare che il vostro programma è ancora corretta. Ad esempio, invece di fare questo:

synchronized (a) {
  doSomethingWith(a);
  longMethodNothingToDoWithA();
  doSomethingWith(a);
}

prendere in considerazione fare questo se e solo se il programma sarà ancora corretto:

synchronized (a) {
  doSomethingWith(a);
}
longMethodNothingToDoWithA();
synchronized (a) {
  doSomethingWith(a);
}

Ma ricordate, l'aggiornamento campo semplice dispari con un blocco tenuto inutilmente probabilmente non farà molta differenza tangibile, e potrebbe effettivamente migliorare le prestazioni. A volte, in possesso di un blocco per un po 'di più e fare meno di blocco "di pulizia" può essere utile. Ma la JVM può fare alcune di quelle decisioni , quindi non c'è bisogno di essere tooo paranoid-- basta fare le cose in generale sensibili e si dovrebbe andare bene.

In generale, cercare di avere una serratura separata per ogni insieme di metodi / accessi che insieme formano un "processo indipendente". Oltre a questo, avere un oggetto di blocco separato può essere un buon modo di incapsulante la serratura all'interno della classe è usato da (ad esempio impedendo che venga utilizzato dai chiamanti esterni in un modo che non ha predetto) , ma probabilmente non c'è differenza di prestazioni di per sé di utilizzare un oggetto all'altro, come il blocco (ad esempio utilizzando l'istanza stessa vs un oggetto privato dichiarata solo per essere un blocco all'interno di tale classe come lei suggerisce), a condizione che sarebbero altrimenti utilizzati i due oggetti esattamente nello stesso modo.

Ci dovrebbe essere una differenza di prestazioni tra un built-in costrutto del linguaggio e una biblioteca, ma l'esperienza mi ha insegnato a non indovinare quando si tratta di prestazioni.

Se si compila l'aspetto nell'applicazione allora si avrà praticamente nessun calo di prestazioni, se lo si fa in fase di esecuzione (carico-tipo di tessitura), poi si vedrà un calo di prestazioni.

Se si dispone di ogni aspetto essere perinstance allora può ridurre la necessità per la sincronizzazione.

Si dovrebbe avere il meno possibile la sincronizzazione, per il più breve tempo possibile, per ridurre eventuali problemi.

Se possibile, si consiglia di condividere il meno Stato possibile tra i thread, mantenendo il più locale possibile, per ridurre i problemi di deadlock.

Maggiori informazioni porterebbe ad una risposta migliore btw. :)

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