2012-11-28 13 views
13

Durante l'utilizzo di più thread, ho imparato a utilizzare le variabili statiche ogni volta che desidero utilizzare un contatore a cui si accede da più thread.Java: utilizzo di AtomicInteger vs Static int

Esempio:

static int count=0; Poi più tardi nel programma Io lo uso come count++;.

Oggi mi sono imbattuto in qualcosa chiamato AtomicInteger e ho anche appreso che è thread sicuro e potrebbe utilizzare uno dei suoi metodi chiamati getAndInrement() per ottenere lo stesso effetto.

Qualcuno potrebbe aiutarmi a capire sull'utilizzo di static atomicInteger rispetto a static int count?

+0

Dovete proteggere accessi concorrenti a un 'int' te stesso. Ma, 'AtomicInteger' è progettato per essere thread-safe. – reprogrammer

+9

'static' non ha nulla a che fare con il multi-threading. –

+0

Per quanto ne so, utilizziamo variabili volatili anziché variabili statiche in ambienti con multithreading. – Vishal

risposta

20

-AtomicInteger viene utilizzato per eseguire l'operazione atomica su un intero, la sua alternativa quando non si desidera utilizzare synchronized parola chiave.

- Utilizzando un volatile su un campo non-atomica darà risultato incoerente.

int volatile count; 

public void inc(){ 

count++ 

} 

-static farà una variabile condiviso da tutte le istanze di tale classe, ma ancora produrrà un risultato incoerente in ambiente multi-threading.

Quindi, cercare questi quando ci si trova in ambiente multithreading:

1. sua sempre meglio seguire la regola del Brian:

Quando mai si scrive una variabile che è accanto a leggere da un altro thread , o quando stiamo leggendo una variabile che è scritta solo da un altro thread, deve essere sincronizzato. I campi condivisi devono essere riservati a , rendendo sincronizzati i metodi di lettura e scrittura/le dichiarazioni atomiche .

2. Seconda opzione utilizza la Atomic Classes, come AtomicInteger, AtomicLong, AtomicReference, etc.

+0

Grazie Kumar !!!!! – user547453

+0

@ user547453 Prego ........... –

1

Con AtomicInteger il incrementAndGet() garantito per essere atomico.
Se si utilizza count++ per ottenere il valore precedente, non è garantito che sia atomico.


Qualcosa l'ho persa dalla tua domanda - ed è stato dichiarato da altra risposta - static non ha nulla a che fare con filettatura.

+0

cosa intendi per "atomico"? – user547453

+2

Vedere http://en.wikipedia.org/wiki/Atomicity_(programming) – reprogrammer

+1

significa, ad esempio, che se due thread stanno usando, per esempio 'prevCount = count ++' nello stesso tempo, potrebbe essere che entrambi il thread avrà lo stesso valore di prevCount. –

1

"statico" rende il var come livello di classe. Ciò significa che se si definisce "statico conteggio int" in una classe, indipendentemente dal numero di istanze create dalla classe, tutte le istanze utilizzano lo stesso "conteggio". Mentre AtomicInteger è una classe normale, aggiunge solo la protezione della sincronizzazione.

1

static int counter darebbe risultato incoerente multithreaded ambiente a meno che non si effettua il contatore volatile o fare il blocco di incremento synchronized.

In caso di automic fornisce la programmazione lock-freethread-safe su singole variabili.

Altro dettaglio nel automic's e link

+1

Non penso che la volatilità sia sufficiente per rendere ++ sicuro. –

1

penso che ci sia alcun gurantee di vedere sul count++ il valore più recente. count++ deve leggere il valore di count. Un altro Thread può aver scritto un nuovo valore su count ma memorizzato il suo valore sulla cache locale Thread, i. e. non scorre alla memoria principale. Anche il tuo Thread, che legge count, non ha garanzie da leggere dalla memoria principale, i. e. aggiorna dalla memoria principale. synchronize garantisce che.

+0

Grazie Vertex !!!!! – user547453

1

AtomicInteger consente di ottenere e incrementare come processo atomico. Può essere pensato come un sequencer nel database. Fornisce metodi di utilità per incrementare, decrementare i valori int delta.

statico int può causare problemi se si ottiene contatore e quindi l'elaborazione e quindi l'aggiornamento. AtomicInteger lo fa facilmente ma non è possibile utilizzarlo se è necessario aggiornare il contatore in base all'elaborazione dei risultati.

+0

Grazie Pankaj !!!!! – user547453

5

Sono d'accordo con @ risposta di Kumar.

Volatile non è sufficiente - ha alcune implicazioni per l'ordine di memoria, ma non garantisce l'atomicità di ++.

La cosa veramente difficile della programmazione multi-thread è che i problemi potrebbero non essere visualizzati in una quantità ragionevole di test. Ho scritto un programma per dimostrare il problema, ma ha thread che non fanno altro che incrementare i contatori. Anche così, i conteggi sono all'interno di circa l'1% della risposta giusta. In un programma reale, in cui i thread hanno altro lavoro da fare, ci può essere una probabilità molto bassa di due thread che fanno il ++ abbastanza vicino da mostrare simultaneamente il problema. La correttezza multi-thread non può essere testata, deve essere progettata in.

Questo programma esegue lo stesso compito di conteggio utilizzando un semplice int statico, un volatile int e un AtomicInteger. Solo AtomicInteger ottiene sempre la risposta giusta. Un output tipico su un multiprocessore con 4 core dual-threaded è:

count: 1981788 volatileCount: 1982139 atomicCount: 2000000 Expected count: 2000000 

Ecco il codice sorgente:

import java.util.ArrayList; 
    import java.util.List; 
    import java.util.concurrent.atomic.AtomicInteger; 

    public class Test { 
    private static int COUNTS_PER_THREAD = 1000000; 
    private static int THREADS = 2; 

    private static int count = 0; 
    private static volatile int volatileCount = 0; 
    private static AtomicInteger atomicCount = new AtomicInteger(); 

    public static void main(String[] args) throws InterruptedException { 
     List<Thread> threads = new ArrayList<Thread>(THREADS); 
     for (int i = 0; i < THREADS; i++) { 
     threads.add(new Thread(new Counter())); 
     } 
     for (Thread t : threads) { 
     t.start(); 
     } 
     for (Thread t : threads) { 
     t.join(); 
     } 
     System.out.println("count: " + count + " volatileCount: " + volatileCount + " atomicCount: " 
      + atomicCount + " Expected count: " 
      + (THREADS * COUNTS_PER_THREAD)); 
    } 

    private static class Counter implements Runnable { 
     @Override 
     public void run() { 
     for (int i = 0; i < COUNTS_PER_THREAD; i++) { 
      count++; 
      volatileCount++; 
      atomicCount.incrementAndGet(); 
     } 
     } 
    } 
    } 
+0

Grazie Patricia .... Capisco l'uso di atomicInteger quando devo usare un contatore come indicato nella mia domanda originale. Ma cosa succede se ho 3 thread sincronizzati aggiornando il valore di una variabile come ThreadA, ThreadB e ThreadC (per esempio). In questo caso (come suggerito da Kumar), devo usare un metodo privato sincronizzato per leggere e scrivere il valore. E anche rendere la mia variabile definita come "Statico Volatile". Esempio: 'private volatile static String threadName =" ThreadX ";' – user547453

+0

@ user547453 Se tutti gli accessi a una variabile sono sincronizzati sullo stesso oggetto, non è necessario renderlo volatile. Farlo non avrà alcun effetto o rallenterà il tuo codice inutilmente. Esistono molti modi per rendere una variabile condivisa tra più thread: non deve essere statica. –

+0

Una cosa importante da notare qui: '++' non è un'operazione atomica - è una scorciatoia per 'a = a + 1', che consiste nel leggere il valore e scrivere il valore. Tuttavia, la lettura o l'impostazione del valore È un'operazione atomica su un campo volatile. – mucaho