Threads korrekte Synchronisation bei Vector und ArrayList

redbomber

Bekanntes Mitglied
Hi zusammen, ich habe folgende Frage:
Die folgende Klasse wird in einem Thread bearbeitet, dabei wird doCriticalCode() aufgerufen.
Ein anderer Thread greift am Ende von aussen auf eine Liste dieser Klasse zu.
Jetzt möchte ich wissen wie ich das ganze korrekt synchronisiere.

1. bei einem Vector (der ja schon von sich auch synchonisiert ist:

Java:
public class NewClass {
   Vector<Integer> vector;

   public NewClass() {
      vector = new Vector<Integer>();
   }

   public Vector<Integer> getVector() {
      return vector;
   }

   private void doCriticalCode() {
      for (int i = 0; i < 100; i++) {
         vector.add(i);
      }
   }
   
   public void clearVector(){
      vector.clear();
   }
}

oder bei einer ArrayList:
Java:
public class NewClass {
   ArrayList<Integer> list;

   public NewClass() {
      list = new ArrayList<Integer>();
   }

   public ArrayList<Integer> getVector() {
      return list;
   }

   private void doCriticalCode() {
      for (int i = 0; i < 100; i++) {
         list.add(i);
      }
   }

   public void clearList() {
      list.clear();
   }
}

in beiden Fällen gilt:
wenn von außen auf die Liste/Vector zugegriffen wird, wird nur davon gelesen.

Wie muss ich hier jetzt also korrekt synchronisieren?
 
Wie muss ich hier jetzt also korrekt synchronisieren?
Bei Vector überhaupt nicht (es sei denn, du willst über ihn iterieren, während er bearbeitet werden könnte), bei ArrayList kommts drauf an. Du schreibst:
Ein anderer Thread greift am Ende von aussen auf eine Liste dieser Klasse zu.
Er greift also erst drauf zu, wenn der andere Thread fertig ist? Dann musst du nichts synchronisieren, musst aber sicherstellen, dass die Änderungen an der ArrayList für jeden Thread sichtbar werden. Ich denke, dass dann folgende Änderungen ausreichend sind:
Java:
public class NewClass {
   volatile ArrayList<Integer> list;
 
   // ...
 
   private void doCriticalCode() {
      for (int i = 0; i < 100; i++) {
         list.add(i);
      }
      flush();
   }
 
   private void flush() {
      list = list; // Der Arbeiterthread veröffentlicht damit all seine lokalen Änderungen.
   }
 
   public void clearList() {
      list.clear();
      flush();
   }
}
 
Sagen ja schon der Methodenname und der Kommentar.

Habe das von hier: AngelikaLanger.com - Details zu volatile-Variablen - Angelika Langer Training/Consulting (Abschnitt "volatile-Referenzvariablen")

In dieser Form meckert der Eclipse-Compiler nicht. Entfernt man das
Code:
volatile
, erscheint eine Warnung ("Assignment has no effect.").


Ich kenne das so (und Angelika Langer macht das auch so), dass man ein temporäres Objekt erstellt, die Objekte dort einträgt und danach die Referenz schreibt, die dann den Flush im Speicher auslöst. Das ist m.E. lesbar und eingängig.

Ich könnte mir vorstellen (müsste man mal in der Spec bzw. der JSR nachsehen), dass list=list eine NOOP ist und vom Compiler wegoptimiert wird. Eclipse behauptet zumindest es wäre nicht so. Vermtulich zieht die Optimierung nur bei nicht-volatile Feldern.

Das ganze Beispiel ist aber dröge. Die Liste ist nicht private. Hinz und Kunz kann darauf zugreifen und das ganze inkonsistent werden lassen.

Eine Lösung mit volatile würde ich hier bei der Liste nur wählen, wenn ein erheblicher Performanceunterschied zu normaler Synchronisation bestehen würde. Das explizite Aufrufen von flush() nach schreibenden Operationen ist m.E. ein Codesmell, genau so wie die Zuweisung von list auf list. Guter Code sieht anders aus.
 
Ich wollte auch nur darauf hinaus, dass man nicht zwingend synchronisieren muss, wenn ein Protokoll eingehalten wird.
Am einfachsten macht man hier die Synchronisation wohl über eine [JAPI]CopyOnWriteArrayList[/JAPI].

Ich könnte mir vorstellen (müsste man mal in der Spec bzw. der JSR nachsehen), dass list=list eine NOOP ist und vom Compiler wegoptimiert wird.
Also javac optimiert da nichts, weder mit noch ohne volatile. Eventuell die JVM? Bin auf dem Gebiet Laie.

Das explizite Aufrufen von flush() nach schreibenden Operationen ist m.E. ein Codesmell
Ist doch im Prinzip wie der Aufruf von
Code:
repaint()
in Swing-Anwendungen.
 

Neue Themen


Zurück
Oben