Ricerca…


introduzione

Una raccolta simultanea è una [raccolta] [1] che consente l'accesso da più di un thread contemporaneamente. Filetti diversi possono tipicamente scorrere i contenuti della raccolta e aggiungere o rimuovere elementi. La raccolta è responsabile per garantire che la raccolta non venga corrotta. [1]: http://stackoverflow.com/documentation/java/90/collections#t=201612221936497298484

Collezioni thread-safe

Per impostazione predefinita, i vari tipi di raccolta non sono thread-safe.

Tuttavia, è abbastanza facile creare una raccolta sicura per i thread.

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>());

Quando crei una raccolta thread-safe, non devi mai accedervi tramite la raccolta originale, solo attraverso il wrapper thread-safe.

Java SE 5

A partire da Java 5, java.util.collections ha diverse nuove raccolte thread-safe che non richiedono i vari metodi Collections.synchronized .

List<String> threadSafeList = new CopyOnWriteArrayList<String>();
Set<String> threadSafeSet = new ConcurrentHashSet<String>();
Map<String, String> threadSafeMap = new ConcurrentHashMap<String, String>();

Collezioni simultanee

Le raccolte concorrenti sono una generalizzazione di raccolte thread-safe che consentono un utilizzo più ampio in un ambiente concorrente.

Sebbene le raccolte thread-safe abbiano un'aggiunta o rimozione sicura degli elementi da più thread, non necessariamente hanno un'iterazione sicura nello stesso contesto (potrebbe non essere possibile iterare in modo sicuro attraverso la raccolta in un thread, mentre un altro lo modifica aggiungendo / rimozione di elementi).

È qui che vengono utilizzate le raccolte simultanee.

Poiché l'iterazione è spesso l'implementazione di base di diversi metodi di massa nelle raccolte, come addAll , removeAll o anche la raccolta di copie (tramite un costruttore o altri mezzi), l'ordinamento, ... il caso d'uso per le raccolte concorrenti è in realtà piuttosto ampio.

Ad esempio, Java SE 5 java.util.concurrent.CopyOnWriteArrayList è un'implementazione di Lis t thread-safe e simultanea, i suoi stati javadoc :

Il metodo di iterazione di stile "snapshot" utilizza un riferimento allo stato dell'array nel punto in cui è stato creato l'iteratore. Questo array non cambia mai durante il ciclo di vita dell'iteratore, quindi l'interferenza è impossibile e l'iteratore è garantito non lanciare ConcurrentModificationException.

Pertanto, il seguente codice è sicuro:

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();
        }
    }
}
}

Un'altra raccolta concomitante relativa all'iterazione è ConcurrentLinkedQueue , che afferma:

Gli iteratori sono debolmente coerenti, elementi di ritorno che riflettono lo stato della coda a un certo punto o dalla creazione dell'iteratore. Non gettano java.util.ConcurrentModificationException e possono procedere in concomitanza con altre operazioni. Gli elementi contenuti nella coda dalla creazione dell'iteratore verranno restituiti esattamente una volta.

Si dovrebbe controllare il javadocs per vedere se una raccolta è concomitante, oppure no. Gli attributi dell'iteratore restituiti dal metodo iterator() ("fail fast", "weakly consistent", ...) è l'attributo più importante da cercare.

Filetto di esempi sicuri ma non simultanei

Nel codice sopra, cambiando la dichiarazione LIST su

public static final List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());

Potrebbe (e statisticamente sulla maggior parte delle architetture CPU / core moderne) portare a delle eccezioni.

Le raccolte sincronizzate dai metodi di utilità Collections sono thread-safe per l'aggiunta / rimozione di elementi, ma non l'iterazione (a meno che la raccolta sottostante non sia già passata ad essa).

Inserimento 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.
       }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow