2012-11-28 14 views
13

Während der Verwendung von mehreren Threads habe ich gelernt, statische Variablen zu verwenden, immer wenn ich einen Zähler verwenden möchten, auf den mehrere Threads zugreifen.Java - mit AtomicInteger vs Static Int

Beispiel:

static int count=0; Dann später in dem Programm, das ich es als count++; verwenden.

Heute stieß ich auf etwas namens AtomicInteger und ich lernte auch, dass es Thread sicher ist und eine seiner Methoden namens getAndInrement() verwenden könnte, um den gleichen Effekt zu erzielen.

Könnte jemand mir helfen, über die Verwendung von static atomicInteger gegenüber static int count zu verstehen?

+0

Sie haben gleichzeitige Zugriffe auf ein 'int' sich zu schützen. Aber "AtomicInteger" ist threadsicher konstruiert. – reprogrammer

+9

'static' hat nichts mit Multithreading zu tun. –

+0

Nach meinem Verständnis verwenden wir in Multithread-Umgebungen flüchtige Variablen anstelle von statischen Variablen. – Vishal

Antwort

20

-AtomicInteger wird verwendet, um die atomaren Operation über eine ganze, seine Alternative zu führen, wenn Sie nicht synchronized Schlüsselwort verwenden möchten.

- ein volatile auf einem Nicht-Atom Feld Mit inkonsistent Ergebnis geben wird.

int volatile count; 

public void inc(){ 

count++ 

} 

-static eine Variable von allen Instanzen dieser Klasse, aber noch geteilt macht es wird ein uneinheitliches Ergebnis in Multi-Threading-Umgebung erzeugen.

diese So versuchen, wenn Sie in Multithreading-Umgebung:

1. Es ist immer besser, um die Regel Brian zu folgen:

Wann immer wir eine Variable schreiben, die nächste soll gelesen werden von einem anderen Thread, oder wenn wir eine Variable lesen, die nur von einen anderen Thread geschrieben wird, muss es synchronisiert werden. Die freigegebenen Felder müssen private gemacht werden, wodurch die Lese- und Schreibmethoden/atomaren Anweisungen synchronisiert werden.

2. zweite Option ist mit der Atomic Classes, wie AtomicInteger, AtomicLong, AtomicReference, etc.

+0

Danke Kumar !!!!! – user547453

+0

@ user547453 Sie sind herzlich willkommen ........... –

1

Mit AtomicInteger ist die incrementAndGet() garantiert atomar.
Wenn Sie count++ verwenden, um den vorherigen Wert zu erhalten, ist dies nicht garantiert atomar.

Etwas, das ich von Ihrer Frage vermisste - und durch andere Antwort festgestellt wurde - hat nichts mit Threading zu tun.

+0

was meinst du mit "atomar"? – user547453

+2

Siehe http://en.wikipedia.org/wiki/Atomicity_(Programming) – reprogrammer

+1

bedeutet, dass zum Beispiel, wenn zwei Threads, zum Beispiel 'prevCount = count ++' zur gleichen Zeit verwenden, beide sein könnten Der Thread erhält den gleichen Wert von prevCount. –

1

"statisch" machen Sie die Var auf Klassenstufe. Das bedeutet, wenn Sie "static int count" in einer Klasse definieren, unabhängig davon, wie viele Instanzen Sie von der Klasse erstellt haben, verwenden alle Instanzen denselben "count". Während AtomicInteger eine normale Klasse ist, fügen Sie einfach einen Synchronisationsschutz hinzu.

1

static int counter würden Ihnen inkonsistentes Ergebnis in multithreaded Umgebung geben, wenn Sie den Zähler volatile oder machen den Zuwachs Block synchronized machen.

Bei automic gibt es lock-freethread-safe Programmierung auf einzelne Variablen.

Weitere Einzelheiten in automic's und link

+1

Ich glaube nicht, dass volatile ist genug, um ++ sicher zu machen. –

1

Ich denke, es nicht gurantee ist auf count++ der neueste Wert anzuzeigen. count++ muss den Wert count lesen. Ein anderer Thread kann einen neuen Wert auf count geschrieben haben, aber seinen Wert auf dem Thread lokalen Cache gespeichert haben, d. e. Spült nicht zum Hauptspeicher. Auch Ihre Thread, die count liest, hat keine Garantie aus dem Hauptspeicher zu lesen, ich. e. Aktualisierung aus dem Hauptspeicher synchronize garantiert das.

+0

Dank Vertex !!!!! – user547453

1

AtomicInteger ist das Holen und Inkrementieren als atomarer Prozess. Es kann als Sequencer in der Datenbank gedacht werden. Es bietet Utility-Methoden zum Erhöhen und Dekrementieren von Delta-Int-Werten.

static int kann das Problem verursachen, wenn Sie einen Zähler erhalten und ihn dann verarbeiten und dann aktualisieren. AtomicInteger macht es leicht, aber Sie können es nicht verwenden, wenn Sie den Zähler basierend auf Verarbeitungsergebnissen aktualisieren müssen.

+0

Danke Pankaj !!!!! – user547453

5

ich mit @ Kumar Antwort zustimmen.

Volatile ist nicht ausreichend - es hat einige Auswirkungen auf die Speicherordnung, aber nicht Atomizität von ++.

Die wirklich schwierige Sache über Multithread-Programmierung ist, dass Probleme nicht in einer angemessenen Menge von Tests angezeigt werden können. Ich habe ein Programm geschrieben, um das Problem zu demonstrieren, aber es hat Threads, die nur Zähler inkrementieren. Trotzdem liegen die Zählungen innerhalb von etwa 1% der richtigen Antwort. In einem realen Programm, in dem die Threads andere Aufgaben zu erledigen haben, kann es eine sehr geringe Wahrscheinlichkeit geben, dass zwei Threads das ++ nahe genug machen, um gleichzeitig das Problem zu zeigen. Die Multi-Thread-Korrektheit kann nicht getestet werden, sie muss entworfen werden.

Dieses Programm führt die gleiche Zählaufgabe mit einem einfachen statischen int, einem flüchtigen int und einem AtomicInteger aus. Nur der AtomicInteger bekommt immer die richtige Antwort. Eine typische Ausgabe auf einem Multi-Prozessor mit 4 Dual-Gewindekerne ist:

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

Hier ist der Quellcode:

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

Danke Patricia .... Ich verstehe die Verwendung von AtomicInteger, wenn ich einen Zähler wie in meiner ursprünglichen Frage angegeben verwenden muss. Aber was passiert, wenn ich 3 synchronisierte Threads habe, die den Wert einer Variablen wie ThreadA, ThreadB und ThreadC (zum Beispiel) aktualisieren. In diesem Fall muss ich (wie Kumar vorgeschlagen hat) eine private Synchronized-Methode verwenden, um den Wert zu lesen und zu schreiben. Und mache meine Variable auch als 'Static Volatile' definiert. Beispiel: 'private volatile static String threadName =" ThreadX ";' – user547453

+0

@ user547453 Wenn alle Zugriffe auf eine Variable auf dasselbe Objekt synchronisiert sind, ist es nicht notwendig, sie flüchtig zu machen. Dies hat entweder keine Auswirkungen oder verlangsamt den Code unnötig. Es gibt viele Möglichkeiten, um eine Variable zwischen mehreren Threads zu teilen - sie muss nicht statisch sein. –

+0

Eine wichtige Sache, die hier zu beachten ist: '++' ist keine atomare Operation - es ist eine Kurzschrift für 'a = a + 1', die darin besteht, den Wert zu lesen und den Wert zu schreiben. Das Lesen oder Festlegen des Werts ist jedoch eine atomare Operation in einem flüchtigen Feld. – mucaho