Java JDK에 동시 목록이 있습니까?


277

인덱스로 요소에 액세스 할 수있는 동시 List 인스턴스를 작성하려면 어떻게해야합니까? JDK에 사용할 수있는 클래스 또는 팩토리 메소드가 있습니까?


28
건설적인 이유는 무엇입니까? .Net에없는 여러 제안 된 CopyOnWriteArrayList입니다. 두 질문이 서로 관련되어 있지만 닫지 말라고 말할 수 있습니다 !!!
AlikElzin-kilaka

1
Jarrod Roberson이 왜 Stephan의 세부 편집 내용을 가져 와서 잘못 표현 된 원래 질문으로 되 돌리는 것이 좋은 생각이라고 생각하는지 모르겠습니다. Jarrod의 답변은 여전히 ​​완벽히 허용되는 답변입니다. 실제로 CopyOnWriteArrayList는 JDK에서 List를 구현하는 유일한 동시 클래스입니다. 의아해 ...
매트 파셀

10
허용 대답은에 있었기 때문에 원래의 질문 과 스테판는 것을 소스 코드의 무리와 함께 전혀 관계가없는 질문을 넣어 원래의 포스터는 포함하지 않았다 어디 댄 다른 일을 제안했다 추가 답변 발생 완전히 질문을 변경, List구체적으로 어떤 원본을 기물 파손으로 간주 되는 요구 사항 이라고 말합니다 . 중재자가 답변이 해체 된 버전의 질문에 답변하지 않는다고 불평하는 사람들로 인해 이미 질문을 잠갔습니다.

1
/ locked/ closed/ 이전 의견

8
이 질문을 마감 할 이유가 없습니다. JDK의 클래스에 대해 묻습니다. 라이브러리를 검색하는 것과는 다릅니다. Java의 기반입니다.
maaartinus

답변:


175

java.util.concurrent 에는 동시 목록 구현이 있습니다. 특히 CopyOnWriteArrayList 입니다.


84
모든 인서트에 전체 목록을 복사하므로 종종 비효율적입니다.
dfrankow

22
@dfrankow 그러나 업데이트하는 것보다 훨씬 더 많이 반복하면 효율적일 수 있습니다.
b1nary.atr0phy

여기에 표시된대로 제대로 작동하지 않습니다. addAll 메소드를 사용하고 스트림을 사용하여 읽는 경우에도 예외가 있습니다. stackoverflow.com/questions/1527519/…
devssh

166

인덱스 기반 액세스에 신경 쓰지 않고 List의 삽입 순서 유지 특성을 원한다면 java.util.concurrent.ConcurrentLinkedQueue를 고려할 수 있습니다. Iterable을 구현하므로 모든 항목을 추가 한 후에는 Enhanced for 구문을 사용하여 컨텐츠를 반복 할 수 있습니다.

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
나는 (문 간단하게 생각 :foreach 문이라고합니다) : docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka

2
@ AlikElzin-kilaka 당신이 맞아요. 실제 구문에는 "각"이라는 단어가 포함되어 있지 않기 때문에 이름이 항상 나를 귀찮게했다고 생각하지만 공식 이름을 사용하도록 답변을 업데이트 할 것입니다. :)
Matt Passell

3
@ AlikElzin-kilaka Nitpicking, 그러나 JLS 버전 8 에 따르면이를 "향상된 기능"이라고합니다. 자바 튜토리얼 에서도 마찬가지입니다 .
Roland

1
@Roland는 확실히 nitpicking하지 않습니다. Java에서 "각"과 "향상된"사이에는 (현재) 차이가 있습니다.
hfontanez

2
@Roland 간접적으로. Stream.forEach와 현재 향상된 기능 간의 혼동을 없애기 위해 "for each loop"의 이름을 "enhanced for"로 바꿨습니다.
hfontanez

128

간단한 호출 동기화 만 있으면 Collections.synchronizedList (List)를 매우 잘 사용할 수 있습니다 .

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
결과 synchronizedList는 "동기화"이지만 "동시"는 아닙니다. 인덱스 기반의 많은 List 작업이 그 자체가 원자 적이 지 않으며 더 큰 상호 배제 구성의 일부가되어야한다는 근본적인 문제입니다.

6
IMO Vector는 a 보다 더 간단 Collections.synchronizedList(new ArrayList<Object>())합니다.
Stephan

8
synchronizedList의 결과는 "동기화"이지만 "동시"는 아닙니다.
Kanagavelu Sugumar

46

위치를 획득하고 주어진 위치에서 요소를 가져 오는 작업은 자연스럽게 약간의 잠금이 필요하기 때문에 (두 목록 사이에 구조적인 변화가있을 수는 없습니다).

동시 수집의 개념은 각 작업 자체가 원자 적이며 명시적인 잠금 / 동기화없이 수행 될 수 있다는 것입니다.

따라서 원자 적 연산 n으로 주어진 위치 에서 요소를 얻는 것은 List동시 액세스가 예상되는 상황에서 너무 의미가 없습니다.


6
Joachim, 나는 당신이 머리에 못을 박았다고 생각합니다. 예를 들어, 읽기 전용 목록을 동시 목록으로 사용하십시오. N 위치에서 요소를 목록에서 가져 오는 것은 의미가있을뿐 아니라 문제의 핵심입니다. 따라서 불변 목록 (소문자 L)이 좋은 예이지만 목록 (대문자 L)은 아닙니다. CopyOnWriteArrayList는 동시에 사용되지만 많은 사람들이 성능을 좋아하지 않습니다. 로프 라인 (끈 로프)을 따르는 솔루션은 아마도 좋은 승자가 될 것입니다.
johnstosh

1
아주 좋은 지적입니다. 그러나 OP가 사용할 목록은 매우 구체적인 사용법을 가질 수 있습니다. 예를 들어 동시 환경에서 채워져 "잠긴"(무엇이든) 인덱스로 안전하게 액세스 할 수 있습니다. 따라서 첫 번째 단계에서 이러한 List를 작성하려면 스레드 안전 구현이 필요합니다. 불행히도 OP는 찾고있는 List가 어떻게 사용되는지에 대해 구체적이지 않았습니다.
igor.zh

13

다음과 같은 옵션이 있습니다.

  • Collections.synchronizedList(): 당신은 어떤 포장 할 수 있습니다 List구현 ( ArrayList, LinkedList또는 제 3 자 목록). 모든 방법 (읽기 및 쓰기)에 대한 액세스는를 사용하여 보호됩니다 synchronized. iterator()for 루프를 사용 하거나 향상시킨 경우 수동으로 동기화해야합니다. 반복하는 동안 다른 스레드는 읽지 않아도 완전히 차단됩니다. 각 통화 hasNextnext통화 마다 별도로 동기화 할 수도 있지만 ConcurrentModificationException가능합니다.

  • CopyOnWriteArrayList: 수정하는 데 비용이 많이 들지만 기다리지 않아도 읽을 수 있습니다. 반복자는 던지지 않으며 ConcurrentModificationException반복하는 동안 다른 스레드가 목록을 수정하더라도 반복자가 생성 될 때 목록의 스냅 샷을 반환합니다. 자주 업데이트되지 않는 목록에 유용합니다. addAll업데이트에는 대량 작업 이 선호됩니다. 내부 어레이는 여러 번 복사되지 않습니다.

  • Vector: 매우 비슷 synchronizedList하지만 반복도 동기화됩니다. 그러나 ConcurrentModificationException반복하는 동안 다른 스레드가 벡터를 수정하면 반복자가을 던질 수 있습니다 .

다른 옵션:

  • Collections.unmodifiableList(): 잠금이없고 스레드 안전하지만 수정할 수 없음
  • Queue또는 Deque목록 끝에 만 추가 / 제거하고 목록을 반복하는 경우 대안이 될 수 있습니다. 인덱스로 액세스 할 수 없으며 임의의 위치에서 추가 / 제거 할 수 없습니다. 그들은 더 나은 성능과 더 나은 동시 액세스로 여러 개의 동시 구현을 가지고 있지만이 질문의 범위를 벗어납니다. JCTools 도 살펴볼 수 있습니다. 여기에는 단일 소비자 또는 단일 생산자에 특화된 더 많은 성능의 큐 구현이 포함되어 있습니다.

4

여기에 이미지 설명을 입력하십시오

CopyOnWriteArrayList는 기본 배열의 새로운 복사본을 만들어 모든 변이 연산 (추가, 설정 등)이 구현되는 ArrayList의 스레드 안전 변형입니다.

CopyOnWriteArrayList는 동기화 된 List가 List 인터페이스와 java.util.concurrent 패키지의 일부 및 스레드 안전 콜렉션을 구현하는 동시 대안입니다.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList는 페일 세이프이며 반복하는 동안 기본 CopyOnWriteArrayList가 수정 될 때 별도의 ArrayList 사본을 사용하는 경우 ConcurrentModificationException을 발생시키지 않습니다.

복사 배열은 복제 된 복사본이 생성 될 때마다 모든 업데이트 작업을 수행하기 때문에 비용이 많이 듭니다. CopyOnWriteArrayList는 잦은 읽기 작업에만 최선의 선택입니다.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.