0

Wenn das folgende Snippet Ausführen ich sehe, dass die Ergebnisse richtig sind immer und eindeutig inkrementierten jedoch sind sie in der falschen Reihenfolge gedruckt (siehe unten):Garantieren Atom in Java Ordnung oder nur Eindeutigkeit?

import java.util.concurrent.atomic.AtomicInteger; 

public class Atomics { 

    private AtomicInteger index = new AtomicInteger(0); 

    public static void main(String [] args) { 
     new Atomics().updateIndexViaCAS(); 
    } 

    private void updateIndexViaCAS() { 

     Runnable target =() -> { 
      for(int i = 0; i<10;i++) { 
       cas: 
       while (true) { 
        int oldValue = index.get(); 
        int newValue = oldValue + 1; 
        if(index.compareAndSet(oldValue, newValue)) { 
         System.out.println(Thread.currentThread() + ": "+newValue); //order is not guaranteed? 
         break cas; 
        } 
       } 
      } 
     }; 

     /*Runnable target =() -> { 
      for (int i = 0; i < 10; i++) { 
       int oldValue = index.get(); 
       int newValue = oldValue + 1; 
       do { 
        oldValue = index.get(); 
       } while (!index.compareAndSet(oldValue, newValue)); 
       System.out.println(Thread.currentThread() + ": "+newValue); 
      } 
     };*/ 

     for(int t=0; t<2;t++) { 
      Thread th = new Thread(target); 
      th.start(); 
     } 


    } 
} 

Probenergebnisse:

Thread[Thread-0,5,main]: 1 
Thread[Thread-0,5,main]: 3 
Thread[Thread-0,5,main]: 4 
Thread[Thread-0,5,main]: 5 
Thread[Thread-0,5,main]: 6 
Thread[Thread-1,5,main]: 2 <-- out of order 
Thread[Thread-0,5,main]: 7 
Thread[Thread-0,5,main]: 9 
Thread[Thread-0,5,main]: 10 
Thread[Thread-0,5,main]: 11 
Thread[Thread-0,5,main]: 12 
Thread[Thread-1,5,main]: 8 <-- out of order 
Thread[Thread-1,5,main]: 13 
Thread[Thread-1,5,main]: 14 
Thread[Thread-1,5,main]: 15 
Thread[Thread-1,5,main]: 16 
Thread[Thread-1,5,main]: 17 
Thread[Thread-1,5,main]: 18 
Thread[Thread-1,5,main]: 19 
Thread[Thread-1,5,main]: 20 

Ist es weil:

  1. Es ist etwas falsch mit dem Code (und wenn ja, wie es zu beheben, um die Bestellung zu erzwingen)?

  2. Ist es das richtige Verhalten von Atomics, weil sie vom Design her nicht die Ausführungsreihenfolge garantieren, sondern nur die Lockless-Concurrency garantieren?

  3. Gibt es noch etwas anderes hier?

TIA.

Um meine Verwirrung mit den Kommentaren unten zu beheben - warum sollte ich die Atomics in erster Linie brauchen, wenn ich noch Synchronisation verwenden muss, um zu sehen, was unten so weitergeht?

Runnable target =() -> { 
    for (int i = 0; i < 10; i++) { 
     cas: while (true) { 
      synchronized (mutex) { 
       int oldValue = index.get(); 
       int newValue = oldValue + 1; 
       if (index.compareAndSet(oldValue, newValue)) { 
        System.out.println(Thread.currentThread() + ": " 
          + newValue); // order is not guaranteed? 
        break cas; 
       } 
      } 
     } 
    } 
}; 
+7

Die Reihenfolge, in der "index" seine Werte annimmt, ist nicht unbedingt die Reihenfolge, in der die 'println' Aufrufe auftreten.' CompareAndSet' ist atomar; 'compareAndSet'-and-println ist nicht. – user2357112

+0

Es ist # 2. Atomare Variablen erzwingen die Atomizität von Updates und die Speichersichtbarkeit von Updates. Sie haben nichts mit Bestellungen zu tun. –

+0

@ 4castle: Also, bedeutet es, dass das atomare CAS wirklich nichts kauft, da ich noch synchronisieren muss, um zu sehen, was ich über 'println' sehen möchte? Oder was? Beispiele für Code werden geschätzt. –

Antwort

1

Es gibt keine Garantie, wie Ihr Code verschachtelt. In Ihrem Fall denke ich, dass die Schleife nicht als eine Einheit ausgeführt wird, sondern in irgendeiner Weise verschachtelt werden kann. Stellen Sie sich Thread 1 vor der Ausführung von System.out.println(Thread.currentThread() + ": " + newValue) vor, wobei newValue von 0 auf 1 erhöht wurde, so dass nichts gedruckt wird. Thread 2 kann dann von 1 zu 2 inkrementieren und seine outpout vor dem anderen Thread drucken. Dies führt dazu, dass die größere Nummer zuerst gedruckt wird.

Denken Sie auch daran, dass System.out.println auf System.out synchronisiert wird. Dies ist der Grund, warum Sie immer Stücke von Abzügen in der richtigen Reihenfolge beobachten. Ein Thread findet wahrscheinlich System.out gesperrt und unterbindet seine Aktivität für kurze Zeit, bevor er seine Aktivität wieder aufnimmt.

Verwandte Themen