2017-01-18 4 views
1

맵이 키와 유형별 같은 이벤트에 대한 이벤트 리스너의 목록을 보유하고있는에서 넣어왔다 다른 스레드에서 항목을 제거 할 수 있도록하는 데 도움을으로 CopyOnWriteArrayList 수 있습니다 지도를 만들고 모든 리스너와 함께 이벤트를 처리하도록 목록을 반복합니다.는</p> <p><code>func_1()</code>는 한 종류의 listenerlist에서 얻을 시작하는 반복자

한 명의 청취자가 처리를 완료하면지도의 listener 목록에서 제거하도록 요청합니다.

청취자가 반복기에 있으므로 원래 목록에서 제거하면 iterator.previous()java.util.ConcurrentModificationException이 다음 청취자가됩니다.

질문 : 목록의 사본이기 때문에 CopyOnWriteArrayList를 사용하여 수신기 목록을 복사 한 다음 반복자를 복사하면 수신기가 다른 스레드에서 제거 될 때 여전히 throw됩니까?

이터레이터에 CopyOnWriteArrayList 대신 일반 목록을 복사하는 것만으로 차이가 있습니까?

func_1(Event event) { 

    List<WeakReference<EventListener<Event>>> listenerlist = mEventMap.get(event.eventType); 

    /* instead of directly iterator on the listenerlist 
    ListIterator<WeakReference<EventListener<Event>>> listenerIterator = 
     listenerlist.listIterator(listenerlist.size()); 
    but making a CopyOnWriteArrayList first: 
    */ 
    List<WeakReference<EventListener<Event>>> listeners = 
         new CopyOnWriteArrayList<>(listenerlist); 

    ListIterator<WeakReference<EventListener<Event>>> listenerIterator = 
      listeners.listIterator(listeners.size()); 

    while(listenerIterator.hasPrevious()){ 
     WeakReference<EventListener<Event>> listenerItem = 
             listenerIterator.previous(); 
     //doing something 
     listenerItem.func_2(event); 
    } 
} 

EventListener::func_2(Event event){ 
    //do something 
    //remove the type in the map 

    funct_3(EventListener.this); 

} 

funct_3(EventListener listener) { 
    List<WeakReference<EventListener<Event>>> listeners = 
      mEventMap.get(listener.eventType); 

     if (listeners != null) { 
      Iterator<WeakReference<EventListener<Event>>> listenerIterator = 
             listeners.iterator(); 
      while (listenerIterator.hasNext()) { 
       WeakReference<EventListener<Event>> listenerItem = listenerIterator.next(); 
       if (listenerItem.get() != null && listenerItem.get() == listener) { 
        listenerIterator.remove(); 
        break; 
       } 
      } 
     } 
} 
+0

테스트하기가 쉽지만, CopyOnWriteArrayList는 결코 ConcurrentModificationException을 throw합니다. – shmosel

답변

0

테스트는 원래 목록에서 제거가 수행되는 동안 목록의 복사본을 반복하므로 테스트가 실패하지 않습니다.

이벤트가 너무 자주 발생하면 비용이 많이 드는 일이 있습니다.

- 배열의 새로운 카피를 작성하는이 https://www.ibm.com/developerworks/library/j-5things4/

"2.으로 CopyOnWriteArrayList 시간과 메모리 오버 헤드 모두의 측면에서, 작업이 너무 비싸, 일반적인 사용에 대한 고려, 개발자는 종종 동기화 사용에 의존 그러나 컬렉션의 내용을 반복 할 때마다 일관성을 유지하기 위해 읽기 및 쓰기를 포함한 모든 작업을 동기화해야하기 때문에 비용이 많이 드는 옵션이됩니다. 이렇게하면 수많은 시나리오 독자는 ArrayList를 읽고 있지만 수정하는 사람은 거의 없습니다. CopyOnWriteArrayList는이 문제를 해결하는 놀라운 작은 보석입니다. Javadoc은 CopyOnWriteArrayList를 "배열의 새로운 복사본을 만들어 모든 변형 작업 (추가, 설정 등)이 구현되는 ArrayList의 스레드 안전 변형"으로 정의합니다. 컬렉션이 내용을 새 배열로 내부적으로 복사합니다. 모든 수정시 어레이의 내용에 액세스하는 독자는 (변경 가능한 데이터에서 절대로 작동하지 않으므로) 동기화 비용이 발생하지 않습니다. 기본적으로 CopyOnWriteArrayList는 ArrayList가 실패하는 정확한 시나리오에 이상적입니다. JavaBean 이벤트의 Listener와 같이 드물게 읽기 - 쓰기가 거의없는 모음 "