수색…


소개

동시 수집 (concurrent collection)동시에 하나 이상의 쓰레드에 의한 접근을 허용하는 [collection] [1]이다. 다른 스레드는 일반적으로 컬렉션의 내용을 반복하고 요소를 추가하거나 제거 할 수 있습니다. 컬렉션은 컬렉션이 손상되지 않도록하는 책임이 있습니다. [1] : http://stackoverflow.com/documentation/java/90/collections#t=201612221936497298484

thread-safe 컬렉션

기본적으로 다양한 Collection 형식은 스레드로부터 안전하지 않습니다.

그러나 컬렉션을 스레드로부터 안전하게 만드는 것은 매우 쉽습니다.

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

스레드로부터 안전한 컬렉션을 만들 때는 원본 컬렉션을 통해 스레드 안전 래퍼를 통해서만 액세스해야합니다.

Java SE 5

Java 5에서부터 java.util.collections 에는 다양한 Collections.synchronized 메소드가 필요없는 여러 개의 새로운 스레드 안전 콜렉션이 있습니다.

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

동시 수집

동시 콜렉션은 동시 환경에서 더 광범위한 사용을 허용하는 스레드 세이프 콜렉션의 일반화입니다.

스레드 안전 컬렉션은 여러 스레드에서 안전하게 요소를 추가하거나 제거 할 수 있지만 동일한 컨텍스트에서 안전하게 반복 할 필요는 없습니다 (하나의 스레드에서 컬렉션을 안전하게 반복 할 수 없거나 다른 스레드에서 컬렉션을 추가 / 요소 제거).

여기에서 동시 콜렉션이 사용됩니다.

반복은 종종 addAll , removeAll 또는 콜렉션 복사 (생성자 또는 다른 방법을 통한), 정렬, 정렬과 같은 여러 가지 대량 메소드의 기본 구현이기 때문에 동시 콜렉션의 사용 사례는 실제로 매우 큽니다.

예를 들어, Java SE 5의 java.util.concurrent.CopyOnWriteArrayList 는, thread에 대해서 안전하고 병행 된 Lis 구현이며, javadoc 에서는 다음과 같이 나타냅니다.

"스냅 샷"스타일 iterator 메소드는 iterator가 생성 된 시점에서 배열의 상태에 대한 참조를 사용합니다. 이 배열은, 반복자의 유효 기간 중에는 변경되지 않기 때문에, 간섭은 불가능하고, 반복자는 ConcurrentModificationException를 throw하지 않는 것이 보증되고 있습니다.

따라서 다음 코드는 안전합니다.

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

반복에 관한 또 다른 동시 수집은 ConcurrentLinkedQueue 이며 다음과 같이 설명합니다.

반복자는 일관성이 약하고, 반복자를 생성 한 시점 이후의 큐의 상태를 반영하는 요소를 반환합니다. 그들은 java.util.ConcurrentModificationException을 던지지 않으며 다른 작업과 동시에 진행할 수 있습니다. 반복자 생성 이후 큐에 포함 된 요소는 정확히 한 번만 반환됩니다.

컬렉션이 병행되어 있는지 확인하려면 javadocs를 확인해야합니다. iterator() 메소드 ( "fail fast", "weakly consistent"등 iterator() 의해 리턴 된 iterator() 속성은 찾을 수있는 가장 중요한 속성입니다.

스레드 안전하지만 비 동시성 예제

위의 코드에서 LIST 선언을

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

가장 현대적인 멀티 CPU / 코어 아키텍처에서 통계적으로 (또는 통계적으로) 예외가 발생할 수 있습니다.

Collections 유틸리티 메소드의 동기화 된 콜렉션은 요소의 추가 / 제거에는 스레드 안전하지만 반복에는 적용되지 않습니다 (기본 콜렉션이 이미 전달되지 않은 경우).

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
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow