2012-11-05 2 views
1

자식이 목록에 저장되는 거대한 트리 구조를 반복하는 단일 스레드 응용 프로그램이 있습니다. 반복자는 항상 변경 불가능한리스트에 운항합니다 : 단일 스레드의 unmodifiableList에서 ConcurrentModificationException

public List<HierarchyNode> getChildren() { 
     return Collections.unmodifiableList(children); 
} 

아직도 내가 변경 불가능한 목록에서 불가능하다고 생각 몇 가지 점에서 ConcurrentModificationException를 얻을? 반복은 방문자를 사용하여 수행됩니다 ... 어떤 아이디어가 가능합니까?

편집 : 어쩌면 이것은 thetree의 메모리 사용량이 상당히 큰 사실과 관련이

private final List<HierarchyNode> children; 

(> 4기가바이트 :이 목록을 수정할 수있는 단 하나의 목록을 잡고 클래스의 생성자입니다)?

추적 :

Testcase: testParserSingleFile(General.NetlistBuilder): Caused an ERROR 
null 
java.util.ConcurrentModificationException 
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819) 
    at java.util.ArrayList$Itr.next(ArrayList.java:791) 
    at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1067) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitArchitectureNode(HierarchyNodeVisitorImplementation.java:20) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitArchitectureNode(NetlistBuilder.java:40) 
    at com.bevm.hierarchy.ArchitectureNode.accept(ArchitectureNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitEntityNode(HierarchyNodeVisitorImplementation.java:33) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitEntityNode(NetlistBuilder.java:33) 
    at com.bevm.hierarchy.EntityNode.accept(EntityNode.java:33) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitComponentNode(HierarchyNodeVisitorImplementation.java:27) 
    at com.bevm.hierarchy.ComponentNode.accept(ComponentNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitInstanceNode(HierarchyNodeVisitorImplementation.java:45) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitInstanceNode(NetlistBuilder.java:85) 
    at com.bevm.hierarchy.InstanceNode.accept(InstanceNode.java:89) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitArchitectureNode(HierarchyNodeVisitorImplementation.java:21) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitArchitectureNode(NetlistBuilder.java:40) 
    at com.bevm.hierarchy.ArchitectureNode.accept(ArchitectureNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitEntityNode(HierarchyNodeVisitorImplementation.java:33) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitEntityNode(NetlistBuilder.java:33) 
    at com.bevm.hierarchy.EntityNode.accept(EntityNode.java:33) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitComponentNode(HierarchyNodeVisitorImplementation.java:27) 
    at com.bevm.hierarchy.ComponentNode.accept(ComponentNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitInstanceNode(HierarchyNodeVisitorImplementation.java:45) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitInstanceNode(NetlistBuilder.java:85) 
    at com.bevm.hierarchy.InstanceNode.accept(InstanceNode.java:89) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitArchitectureNode(HierarchyNodeVisitorImplementation.java:21) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitArchitectureNode(NetlistBuilder.java:40) 
    at com.bevm.hierarchy.ArchitectureNode.accept(ArchitectureNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitEntityNode(HierarchyNodeVisitorImplementation.java:33) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitEntityNode(NetlistBuilder.java:33) 
    at com.bevm.hierarchy.EntityNode.accept(EntityNode.java:33) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitComponentNode(HierarchyNodeVisitorImplementation.java:27) 
    at com.bevm.hierarchy.ComponentNode.accept(ComponentNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitInstanceNode(HierarchyNodeVisitorImplementation.java:45) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitInstanceNode(NetlistBuilder.java:85) 
    at com.bevm.hierarchy.InstanceNode.accept(InstanceNode.java:89) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitGenerateNode(HierarchyNodeVisitorImplementation.java:39) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitGenerateNode(NetlistBuilder.java:79) 
    at com.bevm.hierarchy.GenerateNode.accept(GenerateNode.java:27) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitArchitectureNode(HierarchyNodeVisitorImplementation.java:21) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitArchitectureNode(NetlistBuilder.java:40) 
    at com.bevm.hierarchy.ArchitectureNode.accept(ArchitectureNode.java:25) 
    at com.bevm.hierarchy.HierarchyNodeVisitorImplementation.visitEntityNode(HierarchyNodeVisitorImplementation.java:33) 
    at com.bevm.semantics.netlist.NetlistBuilder.visitEntityNode(NetlistBuilder.java:33) 
    at com.bevm.hierarchy.EntityNode.accept(EntityNode.java:33) 
    at com.beckhoff.vmagic.hierarchy.HierarchyNodeVisitorImplementation.visit(HierarchyNodeVisitorImplementation.java:15) 
    at General.NetlistBuilder.testParserSingleFile(NetlistBuilder.java:125) 
+0

이전에 같은 문제에 직면했습니다. –

+2

반복 코드를 표시 할 수 있습니까? – CAMOBAP

+1

전체 스택 추적을 작성하십시오. –

답변

0

여러분 중 대부분은 옳았습니다. 간단한 대답은 목록이 실제로 변경 불가능하지 않다는 것입니다 ... 동일한 클래스의 모든 객체는 getChildren() 메소드를 사용하지 않고 대신 private 멤버에 직접 액세스합니다. 따라서 코드에서 검색을 계속해야합니다 ... 귀하의 의견이 도움이 될 것입니다.

9

그래서 Collection.unmodifiableList은 정말 스레드 안전하지 않습니다. 이는 기본으로 제공되는 List의 수정 불가능한보기를 만들기 때문입니다. 그러나 뷰가 반복되는 동안 기본 List이 수정되면 CME를 받게됩니다. CME는 별도의 스레드로 인해 생길 필요는 없음을 기억하십시오. 다음과 같은 처리를 할 경우 나는 CME를 얻을 것이다 :

for (String e : myList){ 
    myList.remove(5); // throws CME 
} 

더 나은 선택은 구아바의 전달 된 목록의 불변의 복사본을 생성 ImmutableList입니다. 대한 명확한

인해 의견과 함께 게시 된 코드 교체 :

public List<HierarchyNode> getChildren() { 
     return ImmutableList.copyOf(children); 
    } 

List이 메서드에서 반환하는 CME 던져 결코 수익을 창출한다.

업데이트 :

  1. children의 목록이 이제까지 수정할 수 있습니다 (제가 말하는거다 :

    당신은 여전히 ​​당신의 코드에서 happining 이유를 알아 내려고하는 경우, 다음 사항을 고려 수정 가능 ListunmodifiableList에게 전달됩니까?

  2. getChildren을 호출하는 클래스 중 하나라도 수정 가능 목록을 업데이트 할 수있는 방식으로 호출 할 수있는 클래스를 생성합니까?
  3. 또는 두 번 이상 사용되는 Iterator의 인스턴스를 가져 오는 클래스가 있습니까?

ImmutableList

+0

예를 들어 답을 자세히 설명하여 이해할 수 있도록하십시오. –

+0

스레드 안전성은 여기에서 상당히 관련이없는 것처럼 보입니다. * "단일 스레드 응용 프로그램"*. – assylias

+2

+1 다른 해결책은 복사하는 것입니다.'새로운 ArrayList를 반환합니다. (children); ' –

0

당신은 컬렉션을 수정 한 다음에 기존의 반복자를 사용할 수 없습니다 - 당신은 어딘가에 목록을 수정하려고하는 (그리고 나는 여기뿐만 아니라 원래 목록을 의미)? ConcurrentModificationException에서

:

참고이 예외는 항상 객체가 동시에 다른 스레드에 의해 수정 된 것으로 표시하지 않습니다.

+0

수정할 수없는 경우 어떻게 수정할 수 있습니까? 이 중 하나가 JVM 문제이거나 변경 불가능한 것에 대한 나의 이해가 완전히 잘못되었습니다 ... – BennyBarns

+0

"수정 불가능"은 수정하려고하면 런타임 예외가 발생한다는 의미입니다. 하지만 우리는 실제로 어둠 속에서 촬영하고 있습니다. 왜냐하면 우리에게 스택 추적을 제공하지 않기 때문입니다. – ShiDoiSi

2

은 아마도이 목록을 수정할 수 있습니다 하나는 전혀 수정할 필요가 없습니다이 경우에는 그것을

을 잡고 클래스의 생성자입니다. 나는 당신이 항상 불변으로 만들 것을 제안하고 당신은이 문제를 갖지 않을 것이다. 당신의 생성자에서

당신이

List<HierarchyNode> children = new ArrayList<>(); 
// create/modify collection. 

this.children = Collections.immutableList(children); 

을 할 수있는이 이러한 변경을 할 경우에, 나는 컬렉션이 수정되는 위치를 찾을 수 의심 당신의 방법

public List<HierarchyNode> getChildren() { 
    return children; 
} 

단순화합니다.


문제 Collections.unmodifiableList()이 방법에 의해 리턴 된 참조를 사용하여 변형을 방지한다는 것이다. 래핑하는 콜렉션에 대한 수정을 막지 않습니다.

List<String> words = new ArrayList<String>(); 
words.add("hello"); 
words.add("world"); 
List<String> unmodifiable = Collections.unmodifiableList(words); 
List<String> copy = new ArrayList<String>(words); 

System.out.println("Before modification"); 
System.out.println("words: " + words); 
System.out.println("unmodifiable: " + unmodifiable); 
System.out.println("copy: " + copy); 

words.remove("hello"); 
words.add("hi"); 

System.out.println("\nAfter modification"); 
System.out.println("words: " + words); 
System.out.println("unmodifiable: " + unmodifiable); 
System.out.println("copy: " + copy); 

인쇄

Before modification 
words: [hello, world] 
unmodifiable: [hello, world] 
copy: [hello, world] 

After modification 
words: [world, hi] 
unmodifiable: [world, hi] 
copy: [hello, world] 

새로운 라이브러리를 추가하지 않고이 솔루션은 그것을 반환하기 전에 컬렉션을 복사하는 것입니다.

+0

나는 이것을 점검했다 : 아마도이리스트를 수정할 수있는 유일한 사람은 그것을 회원으로 보유한 클래스의 생성자이다. 서브 클래스를 포함한 다른 모든 것들은이 변경 불가능한 목록을 사용하여 목록에 접근 할 수 있습니다 : – BennyBarns

+0

이 경우 수정을 방지하는 방법에 대한 내 대답을보고 변경 될 곳을 감지 할 수 있습니다. –

1

당신은 아마 다음과 같이 내부의 반복을 목록 을 수정하려고는 :

당신의 논리가 있습니다
for (int i = 0; i < list.size(); i++) { 
if (someCondition) 
    list.remove(i--); 
} 

경우를 대신

Iterator iter = list.iterator(); 
while (iter.hasNext()) { 
    if (someCondition) 
    list.remove(someObject); 
} 

, 이것은 당신이해야 할 일이다 CopyOnWriteArrayList를 사용할 수 있습니다. 이 구현은 수정할 때마다 새로운 목록을 제공합니다. 이는 많은 반복을 수행하지만 목록에 대한 수정이 거의없고 주어진 반복이 최근 수정 사항을 목록에 반영 할 필요가없는 상황에 유용합니다. Dispatcher/구독자 응용 프로그램에서 많이 사용했으며 성능이 뛰어납니다.

+1

편집 : 목록을 정말로 수정할 수 없다면 위의 응답이 분명히 적용되지 않습니다 .... 죄송합니다. – mxns

관련 문제