문제

제가 작업 중인 멀티스레드 애플리케이션에서는 가끔 다음과 같은 현상이 발생합니다. ConcurrentModificationExceptions 우리 목록에 (대부분 ArrayList, 때로는 벡터).그러나 컬렉션을 반복할 때 항목이 누락된 것처럼 보이지만 예외가 발생하지 않기 때문에 동시 수정이 발생하고 있다고 생각하는 경우도 있습니다.나는에 대한 문서를 알고 ConcurrentModificationException 당신은 그것에 의존할 수 없다고 말하지만 동시에 목록을 수정하지 않도록 하려면 어떻게 해야 합니까?그리고 컬렉션에 대한 모든 액세스를 동기화된 블록으로 래핑하는 것이 이를 방지하는 유일한 방법입니까?

업데이트: 응, 나도 알아 Collections.synchronizedCollection, 그러나 컬렉션을 반복하는 동안 누군가 컬렉션을 수정하는 것을 방지하지는 않습니다.내가 컬렉션을 반복하는 동안 누군가가 컬렉션에 무언가를 추가하면 내 문제 중 적어도 일부가 발생한다고 생각합니다.

두 번째 업데이트 누군가가 동기화된 컬렉션에 대한 언급과 Jason처럼 복제를 java.util.concurrent 및 jacekfoo 및 Javamann과 같은 아파치 컬렉션 프레임워크에 대한 언급과 결합하고 싶다면 대답을 받아들일 수 있습니다.

도움이 되었습니까?

해결책

귀하의 원래 질문은 스레드 안전을 유지하면서 기본 컬렉션에 대한 실시간 업데이트를 보는 반복자를 요구하는 것 같습니다.이는 일반적인 경우에 해결하기에는 엄청나게 비용이 많이 드는 문제이므로 표준 컬렉션 클래스 중 어느 것도 이를 수행하지 않습니다.

문제에 대한 부분적인 해결책을 얻는 방법에는 여러 가지가 있으며, 애플리케이션에서는 그 중 하나만으로도 충분할 수 있습니다.

제이슨이 준다 스레드 안전성을 달성하고 ConcurrentModificationException 발생을 방지하는 특정 방법, 그러나 생명력이 희생되는 경우에만 가능합니다.

Javamann이 언급함 두 개의 특정 클래스 에서 java.util.concurrent 확장성이 중요한 잠금 없는 방식으로 동일한 문제를 해결하는 패키지입니다.이는 Java 5에서만 제공되지만 패키지 기능을 이전 Java 버전으로 백포트하는 다양한 프로젝트가 있었습니다. 이 하나, 하지만 이전 JRE에서는 그렇게 좋은 성능을 발휘하지 못합니다.

이미 일부 Apache Commons 라이브러리를 사용하고 있다면 jacekfoo로 로그인하세요. 지적, 아파치 컬렉션 프레임워크 유용한 수업이 포함되어 있습니다.

다음을 살펴보는 것도 고려해 볼 수 있습니다. Google 컬렉션 프레임워크.

다른 팁

업데이트 빈도에 따라 제가 가장 좋아하는 것 중 하나는 CopyOnWriteArrayList 또는 CopyOnWriteArraySet입니다.동시 수정 예외를 피하기 위해 업데이트 시 새 목록/세트를 만듭니다.

확인해 보세요 java.util.concurrent 동시성을 더 잘 처리하도록 설계된 표준 Collections 클래스 버전의 경우.

예, 컬렉션 개체에 대한 액세스를 동기화해야 합니다.

또는 기존 개체 주위에 동기화된 래퍼를 사용할 수 있습니다.보다 Collections.synchronizedCollection().예를 들어:

List<String> safeList = Collections.synchronizedList( originalList );

그러나 모든 코드는 안전한 버전을 사용해야 하며, 그렇더라도 다른 스레드가 수정되는 동안 반복하면 문제가 발생합니다.

반복 문제를 해결하려면 먼저 목록을 복사하세요.예:

for ( String el : safeList.clone() )
{ ... }

보다 최적화되고 스레드로부터 안전한 컬렉션을 보려면 다음도 참조하세요. java.util.concurrent.

일반적으로 반복되는 동안 목록에서 요소를 제거하려고 하면 ConcurrentModificationException이 발생합니다.

이를 테스트하는 가장 쉬운 방법은 다음과 같습니다.

List<Blah> list = new ArrayList<Blah>();
for (Blah blah : list) {
     list.remove(blah); // will throw the exception
}

당신이 어떻게 그 문제를 해결할 수 있을지 모르겠습니다.스레드로부터 안전한 목록을 직접 구현해야 할 수도 있고, 쓰기 위해 원본 목록의 복사본을 만들고 목록에 쓰는 동기화된 클래스를 가질 수도 있습니다.

방어적 복사를 사용하여 하나의 수정 사항을 시도해 볼 수 있습니다. List 다른 사람에게 영향을 미치지 마십시오.

이를 수행하려면 동기화된 블록에서 컬렉션에 대한 액세스를 래핑하는 것이 올바른 방법입니다.표준 프로그래밍 방식에서는 여러 스레드에서 공유되는 상태를 처리할 때 일종의 잠금 메커니즘(세마포어, 뮤텍스 등)을 사용하도록 규정합니다.

그러나 사용 사례에 따라 일반적으로 특정 경우에만 잠그도록 일부 최적화를 수행할 수 있습니다.예를 들어 자주 읽지만 거의 쓰지 않는 컬렉션이 있는 경우 동시 읽기를 허용하지만 쓰기가 진행 중일 때마다 잠금을 적용할 수 있습니다.동시 읽기는 컬렉션이 수정되는 동안에만 충돌을 일으킵니다.

ConcurrentModificationException은 당신이 요구하는 것이 어려운 문제이기 때문에 최선의 노력입니다.액세스 패턴이 목록을 동시에 수정하지 않는다는 것을 증명하는 것 외에 성능을 저하시키지 않고 이를 안정적으로 수행할 수 있는 좋은 방법은 없습니다.

동기화는 동시 수정을 방지할 가능성이 높으며 결국에는 이를 사용하게 되지만 결국 비용이 많이 들 수 있습니다.가장 좋은 방법은 아마도 앉아서 알고리즘에 대해 잠시 생각하는 것입니다.잠금이 없는 솔루션을 찾을 수 없다면 동기화를 사용하세요.

구현을 참조하세요.기본적으로 int를 저장합니다.

transient volatile int modCount;

이는 '구조적 수정'(예: 제거)이 있을 때 증가됩니다.반복자가 modCount가 변경되었음을 감지하면 동시 수정 예외가 발생합니다.

Collections.synchronizedXXX를 통한 동기화는 반복자 안전성을 보장하지 않으며 put, get, set을 통해서만 쓰기 및 읽기를 동기화하므로 좋지 않습니다.

java.util.concurennt 및 Apache 컬렉션 프레임워크를 참조하세요. 쓰기보다 읽기(동기화되지 않음)가 더 많을 때 동시 환경에서 올바르게 작동하도록 최적화된 일부 클래스가 있습니다. FastHashMap을 참조하세요.

목록에 대한 반복을 통해 동기화할 수도 있습니다.

List<String> safeList = Collections.synchronizedList( originalList );

public void doSomething() {
   synchronized(safeList){
     for(String s : safeList){
           System.out.println(s);

     }
   }

}

이렇게 하면 동기화 시 목록이 잠기고 목록을 편집하거나 반복하는 동안 목록에 액세스하려는 모든 스레드가 차단됩니다.단점은 병목 현상이 발생한다는 것입니다.

이렇게 하면 .clone() 메서드에 비해 일부 메모리가 절약되고 반복에서 수행하는 작업에 따라 속도가 더 빨라질 수 있습니다.

Collections.synchronizedList() 명목상 스레드로부터 안전한 목록을 렌더링하며 java.util.concurrent에는 더 강력한 기능이 있습니다.

그러면 동시 수정 예외가 제거됩니다.그러나 효율성에 대해서는 언급하지 않겠습니다.)

List<Blah> list = fillMyList();
List<Blah> temp = new ArrayList<Blah>();
for (Blah blah : list) {
     //list.remove(blah);  would throw the exception
     temp.add(blah);
}
list.removeAll(temp);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top