Aktualisierung der statischen Sammlung in CompleetableFuture # runAsync

8

Vorbedingungen (allgemeine Beschreibung) :

1. statisches Klassenfeld

%Vor%

2. CompletableFuture#runAsync(Runnable runnable,Executor executor)

wird innerhalb aufgerufen static void main(String args[]) Methode

3. Elemente, die zu someCollection innerhalb von runAsync hinzugefügt wurden, rufen Sie von step2

auf

Code-Snippet (spezifische Beschreibung) :

%Vor%

Problembeschreibung:

Wie ich aus dem Gesichtspunkt der Gleichzeitigkeit verstehe, statische Variable kann NICHT zwischen Threads geteilt werden, so dass Daten auf irgendeine Weise verloren gehen können.

Sollte es in volatile geändert werden oder wäre es sinnvoll zu verwenden ConcurrentSkipListSet<String> ?

    
sergionni 03.01.2018, 10:33
quelle

3 Antworten

3

Basierend auf dem Codeausschnitt:

  • volatile ist hier nicht erforderlich, da es auf der Referenzebene funktioniert, während die Aufgaben die Referenz des Sammlungsobjekts nicht aktualisieren, sondern ihren Status mutieren. Wird der Verweis aktualisiert, wurde möglicherweise volatile oder AtomicReference verwendet.

  • Statisches Objekt kann zwischen Threads geteilt werden, aber das Objekt muss Thread-sicher sein. Eine gleichzeitige Sammlung übernimmt die Aufgabe für leichte bis mittlere Belastung.

Aber die moderne Art, dies zu tun, würde Streams beinhalten, anstatt eine gemeinsame Sammlung zu verwenden:

%Vor%     
jihor 09.01.2018 13:22
quelle
3

In Ihrem speziellen Fall gibt es Möglichkeiten, die Thread-Sicherheit für IDs zu garantieren:

  1. Verwenden Sie die threadsichere Auflistung (z. B. ConcurrentSkipListSet, CopyOnWriteArrayList, Collections.synchronizedList (neue ArrayList (), Collections.newSetFromMap (new ConcurrentHashMap ());
  2. Verwenden Sie die Synchronisation wie unten gezeigt.

Beispiele für synchronisierte:

%Vor%

Ich nehme an, dass es am schnellsten wäre, Collections.newSetFromMap (new ConcurrentHashMap () zu verwenden, wenn Sie viele Benutzer-IDs erwarten. Andernfalls wären Sie mit ConcurrentSkipListSet vertraut.

volatile ist hier eine schlechte Wahl. Volatile garantiert Sichtbarkeit, aber nicht Atomizität. Die typischen Beispiele für flüchtige Verwendung sind

%Vor%

In diesem Fall schreiben / lesen Sie nur einmal. Da "a" flüchtig ist, ist garantiert, dass jeder Thread genau 1 oder 2 "sieht" (lesen). Ein anderes (schlechtes Beispiel):

%Vor%

Hier erhöhen wir die Operation (lesen und schreiben) und es gibt verschiedene Ergebnisse von a, weil wir keine Atomizität haben. Deshalb gibt es AtomicInteger, AtomicLong, etc. In Ihrem Fall würden alle Threads die Schreib-IDs sehen, aber sie würden unterschiedliche Werte schreiben und wenn Sie innerhalb der "add" -Methode von ArrayList sehen, sehen Sie etwas wie:

%Vor%

So niemand garantiert Atomizität der Größe, Sie könnten verschiedene ID in einer Array-Zelle schreiben.

    
egorlitvinenko 09.01.2018 17:49
quelle
2

In Bezug auf die Fadensicherheit ist es egal, ob die Variable statisch ist oder nicht. Was zählt, ist

  1. Sichtbarkeit des gemeinsamen Status zwischen Threads.
  2. Sicherheit (Beibehalten von Klasseninvarianten), wenn das Klassenobjekt von mehreren Threads über Klassenmethoden verwendet wird.

Ihr Codebeispiel ist aus Sicht der Sichtbarkeit in Ordnung, weil ids statisch ist und während der Klassenerstellungszeit initialisiert wird. Es ist jedoch besser, sie als final oder volatile zu markieren, abhängig davon, ob ids reference geändert werden kann. Die Sicherheit wird jedoch verletzt, da ArrayList ihre Invarianten in der Multithread-Umgebung nicht vom Design aus bewahrt. Sie sollten also eine Sammlung verwenden, die für den Zugriff durch mehrere Threads ausgelegt ist. Dieses Thema sollte bei der Auswahl helfen.

    
Nikita Gorbachevski 06.01.2018 00:01
quelle