Java Language
Gleichzeitige Sammlungen
Suche…
Einführung
Fadensichere Sammlungen
Standardmäßig sind die verschiedenen Collection-Typen nicht threadsicher.
Es ist jedoch ziemlich einfach, eine Sammlung threadsicher zu machen.
List<String> threadSafeList = Collections.synchronizedList(new ArrayList<String>());
Set<String> threadSafeSet = Collections.synchronizedSet(new HashSet<String>());
Map<String, String> threadSafeMap = Collections.synchronizedMap(new HashMap<String, String>());
Wenn Sie eine threadsichere Sammlung erstellen, sollten Sie niemals über die ursprüngliche Sammlung, sondern nur über den fadensicheren Wrapper darauf zugreifen.
Ab Java 5 gibt es in java.util.collections
mehrere neue thread-sichere Sammlungen, die die verschiedenen Collections.synchronized
Methoden nicht benötigen.
List<String> threadSafeList = new CopyOnWriteArrayList<String>();
Set<String> threadSafeSet = new ConcurrentHashSet<String>();
Map<String, String> threadSafeMap = new ConcurrentHashMap<String, String>();
Gleichzeitige Sammlungen
Gleichzeitige Auflistungen sind eine Verallgemeinerung von Thread-sicheren Auflistungen, die eine breitere Verwendung in einer gleichzeitigen Umgebung ermöglichen.
Während für thread-sichere Auflistungen sichere Elemente hinzugefügt oder aus mehreren Threads entfernt werden, ist für sie nicht unbedingt eine sichere Iteration im selben Kontext erforderlich (eine Collection kann möglicherweise nicht durch die Collection in einem Thread sicher durchlaufen werden, während eine andere sie durch Hinzufügen von / ändert). Elemente entfernen).
Hier werden gleichzeitige Sammlungen verwendet.
Da die Iteration häufig die addAll
mehrerer Massenmethoden in Sammlungen ist, wie addAll
, removeAll
oder auch das Kopieren von Sammlungen (durch einen Konstruktor oder auf andere Weise), Sortieren, ... ist der Anwendungsfall für gleichzeitige Sammlungen tatsächlich ziemlich groß.
Die Java SE 5 java.util.concurrent.CopyOnWriteArrayList
ist beispielsweise eine Thread-sichere und gleichzeitige Lis
t-Implementierung. In ihrem Javadoc heißt es:
Die Iterator-Methode "Snapshot" verwendet eine Referenz auf den Status des Arrays an dem Punkt, an dem der Iterator erstellt wurde. Dieses Array ändert sich während der Lebensdauer des Iterators nie. Daher sind Interferenzen nicht möglich, und es wird garantiert, dass der Iterator keine ConcurrentModificationException auslöst.
Daher ist der folgende Code sicher:
public class ThreadSafeAndConcurrent {
public static final List<Integer> LIST = new CopyOnWriteArrayList<>();
public static void main(String[] args) throws InterruptedException {
Thread modifier = new Thread(new ModifierRunnable());
Thread iterator = new Thread(new IteratorRunnable());
modifier.start();
iterator.start();
modifier.join();
iterator.join();
}
public static final class ModifierRunnable implements Runnable {
@Override
public void run() {
try {
for (int i = 0; i < 50000; i++) {
LIST.add(i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static final class IteratorRunnable implements Runnable {
@Override
public void run() {
try {
for (int i = 0; i < 10000; i++) {
long total = 0;
for(Integer inList : LIST) {
total += inList;
}
System.out.println(total);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Eine weitere gleichzeitige Auflistung bezüglich der Iteration ist ConcurrentLinkedQueue
, die besagt:
Iteratoren sind schwach konsistent und geben Elemente zurück, die den Status der Warteschlange zu einem bestimmten Zeitpunkt bei der Erstellung des Iterators widerspiegeln. Sie lösen keine java.util.ConcurrentModificationException aus und können gleichzeitig mit anderen Vorgängen fortfahren. Elemente, die seit der Erstellung des Iterators in der Warteschlange enthalten sind, werden genau einmal zurückgegeben.
Man sollte die Javadocs überprüfen, um zu sehen, ob eine Sammlung gleichzeitig ist oder nicht. Die Attribute des Iterators, die von der iterator()
-Methode zurückgegeben werden ("Fail Fast", "schwach konsistent", ...), sind das wichtigste Attribut, nach dem gesucht werden soll.
Thread-sichere, aber nicht gleichzeitige Beispiele
Ändern Sie im obigen Code die LIST
Deklaration in
public static final List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());
Kann (und wird statistisch gesehen auf den meisten modernen Multi-CPU / Core-Architekturen) zu Ausnahmen führen.
Synchronisierte Auflistungen aus den Collections
Utility-Methoden sind threadsicher für das Hinzufügen / Entfernen von Elementen, nicht jedoch für die Iteration (es sei denn, die zugrunde liegende Auflistung ist bereits übergeben).
Einfügen in ConcurrentHashMap
public class InsertIntoConcurrentHashMap
{
public static void main(String[] args)
{
ConcurrentHashMap<Integer, SomeObject> concurrentHashMap = new ConcurrentHashMap<>();
SomeObject value = new SomeObject();
Integer key = 1;
SomeObject previousValue = concurrentHashMap.putIfAbsent(1, value);
if (previousValue != null)
{
//Then some other value was mapped to key = 1. 'value' that was passed to
//putIfAbsent method is NOT inserted, hence, any other thread which calls
//concurrentHashMap.get(1) would NOT receive a reference to the 'value'
//that your thread attempted to insert. Decide how you wish to handle
//this situation.
}
else
{
//'value' reference is mapped to key = 1.
}
}
}