Java Language
Gelijktijdige collecties
Zoeken…
Invoering
Draadveilige collecties
Standaard zijn de verschillende Collectietypen niet thread-safe.
Het is echter vrij eenvoudig om een verzameling thread-safe te maken.
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>());
Wanneer u een thread-safe collectie maakt, mag u deze nooit openen via de originele collectie, alleen via de thread-safe wrapper.
Vanaf Java 5 heeft java.util.collections
verschillende nieuwe thread-safe collecties die niet de verschillende Collections.synchronized
methoden nodig hebben.
List<String> threadSafeList = new CopyOnWriteArrayList<String>();
Set<String> threadSafeSet = new ConcurrentHashSet<String>();
Map<String, String> threadSafeMap = new ConcurrentHashMap<String, String>();
Gelijktijdige collecties
Gelijktijdige collecties zijn een generalisatie van thread-safe collecties, die een breder gebruik in een gelijktijdige omgeving mogelijk maken.
Hoewel thread-safe collecties veilig element toevoegen of verwijderen uit meerdere threads, hebben ze niet noodzakelijkerwijs veilige iteratie in dezelfde context (de ene is mogelijk niet in staat om de collectie in de ene thread veilig te doorlopen, terwijl een andere deze wijzigt door / toe te voegen / elementen verwijderen).
Hier worden gelijktijdige collecties gebruikt.
Omdat iteratie vaak de addAll
is van verschillende addAll
in collecties, zoals addAll
, removeAll
, of ook het kopiëren van collecties (via een constructor of op een andere manier), sorteren, ... de use case voor gelijktijdige collecties is eigenlijk vrij groot.
De Java SE 5 java.util.concurrent.CopyOnWriteArrayList
is bijvoorbeeld een thread-veilige en gelijktijdige implementatie van Lis
, de javadoc staat:
De iteratiemethode in "snapshot" -stijl gebruikt een verwijzing naar de status van de array op het moment dat de iterator is gemaakt. Deze array verandert nooit tijdens de levensduur van de iterator, dus interferentie is onmogelijk en de iterator zal gegarandeerd geen ConcurrentModificationException gooien.
Daarom is de volgende code veilig:
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();
}
}
}
}
Een andere gelijktijdige verzameling met betrekking tot iteratie is ConcurrentLinkedQueue
, waarin staat:
Iterators zijn zwak consistent, terugkerende elementen die de status van de wachtrij weerspiegelen op een bepaald punt op of sinds de oprichting van de iterator. Ze gooien java.util.ConcurrentModificationException niet en kunnen gelijktijdig met andere bewerkingen doorgaan. Elementen in de wachtrij sinds het maken van de iterator worden precies één keer geretourneerd.
Men zou de javadocs moeten controleren om te zien of een verzameling gelijktijdig is of niet. De kenmerken van de iterator die worden geretourneerd door de methode iterator()
("fail fast", "zwak consistent", ...) is het belangrijkste kenmerk waarnaar moet worden gezocht.
Voer veilige maar niet-gelijktijdige voorbeelden in
In bovenstaande code wordt de LIST
aangifte gewijzigd in
public static final List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());
Kan (en statistisch gezien op de meeste moderne, multi-CPU / core-architecturen) leiden tot uitzonderingen.
Gesynchroniseerde collecties uit de Collections
zijn veilig voor het toevoegen / verwijderen van elementen, maar niet voor iteratie (tenzij de onderliggende collectie dat al wordt doorgegeven).
Invoegen 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.
}
}
}