Sök…


Introduktion

En samtidig samling är en [samling] [1] som tillåter åtkomst med mer än en tråd samtidigt. Olika trådar kan typiskt iterera igenom innehållet i samlingen och lägga till eller ta bort element. Samlingen ansvarar för att samlingen inte skadas. [1]: http://stackoverflow.com/documentation/java/90/collections#t=201612221936497298484

Tråd-säkra samlingar

Som standard är de olika samlingstyperna inte trådsäkra.

Det är emellertid ganska enkelt att göra en samling trådsäker.

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

När du skapar en tråd-säker samling, ska du aldrig få tillgång till den genom den ursprungliga kollektionen, bara genom den tråd-säkra inpackningen.

Java SE 5

Från och med Java 5 har java.util.collections flera nya tråd-säkra samlingar som inte behöver de olika Collections.synchronized metoderna.

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

Samtidiga samlingar

Samtidiga samlingar är en generalisering av trådsäkra samlingar, som möjliggör en bredare användning i en samtidig miljö.

Medan tråd-säkra samlingar har säkert elementtillägg eller borttagning från flera trådar, har de inte nödvändigtvis säker iteration i samma sammanhang (en kanske inte kan säkert iterera igenom samlingen i en tråd, medan en annan modifierar den genom att lägga till / ta bort element).

Det är här som samtidiga samlingar används.

Eftersom iteration ofta är basimplementeringen av flera bulkmetoder i samlingar, som addAll , removeAll , eller också samlingskopiering (genom en konstruktör eller på annat sätt), sortering, ... är användningsfallet för samtidiga samlingar ganska stort.

Till exempel är Java SE 5 java.util.concurrent.CopyOnWriteArrayList en trådsäker och samtidig Lis t-implementering, dess javadoc anger:

I itatatormetoden "snapshot" -stil använder en referens till arrayens tillstånd vid den punkt som iteratorn skapades. Denna array förändras aldrig under iteratorns livstid, så störningar är omöjliga och iteratorn garanteras inte att kasta ConcurrentModificationException.

Därför är följande kod säker:

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

En annan samtidig samling om iteration är ConcurrentLinkedQueue , som anger:

Iteratorer är svagt konsistenta, och returnerar element som återspeglar könstillstånd vid någon tidpunkt vid eller sedan skapandet av iteratorn. De kastar inte java.util.ConcurrentModificationException och kan fortsätta samtidigt med andra operationer. Element som finns i kön sedan skapandet av iteratorn kommer att returneras exakt en gång.

Man bör kontrollera javadokorna för att se om en samling är samtidig eller inte. Attributen för iteratorn som returneras av metoden iterator() ("misslyckas snabbt", "svagt konsekvent", ...) är det viktigaste attributet att leta efter.

Trädsäkra men icke samtidiga exempel

I ovanstående kod ändrar du LIST deklarationen till

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

Kunde (och statistiskt sett på de flesta moderna, flera CPU / kärnarkitekturer) leda till undantag.

Synkroniserade samlingar från Collections är trådsäkra för tillägg / borttagning av element, men inte iteration (såvida inte den underliggande samlingen som skickas till den redan är).

Insättning i 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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow