이름 목록에서 "hero"를 삭제하려고 하면 ConcurrentModificationException이 발생한다.

리스트를 순회하는 동안 요소를 추가, 삭제, 수정하면 안되기 때문이다.


그래서 Iterator를 사용한다.


그럼 정상적으로 해당 요소는 삭제된다. 이건 싱글 스레드에선 문제가 없어보인다.


근데 Iterator로 순회하는 도중에 다른 스레드에서 List에 접근하여 조작하려 한다면 어떻게 될까..

iterator는 HashTable이건 List건 부모의 내용이 바뀌지 않는 것을 전제로 사용된다 하기 때문에 분명 똑같은 예외가 발생할 것 같다.


방법은 원본 List는 순회하지 않으면 된다.

clone을 사용하여 복제된 List를 순회시키고 해당 조건문에서는 원본 List의 요소를 삭제하면 아무런 문제가 없어보인다.

물론, 더 좋은 방법이 있을지도 모르겠지만! 나중에 한번 더 고민해보지 뭐.. ;p



구글링 하다가 본 내용인데.. 죄다 영어라 이해하기 힘들다.. 아무튼 CopyOnWriteArrayList를 사용하면 위 문제는 해결된다.

하지만 애초에 우린 CopyOnWriteArrayList 보단 ArrayList를 많이 사용하기 때문에 특별한 경우가 아닌 이상에야 un-safe한 리스트를 사용할 이유가 없다고 생각한다.


암튼, 영어가 익숙해지면 한번 쭉- 읽어봅시다.. ( http://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html )


CopyOnWriteArrayList 클래스는 동기화된 List 클래스보다 병렬성을 훨씬 높이고자 만들어졌다. 병렬성이 향상됐고, 특히 List에 들어있는 값을 Iterator로 불러다 사용하려 할 때 List 전체에 락을 걸거나 List를 복제할 필요가 없다.

( CopyOnWriteArrayList 와 비슷하게 Set인터페이스를 구현하는 CopyOnWriteArraySet 도 있다. )


'변경할 때마다 복사'하는 컬렉션 클래스는 불변 객체를 외부에 공개하면 여러 스레드가 동시에 사용하려는 환경에서도 별다른 동기화 작업이 필요 없다는 개념을 바탕으로 스레드 안전성을 확보하고 있다. 하지만 컬렉션이라면 항상 내용이 바뀌어야 하기 때문에, 컬렉션의 내용이 변경될 때마다 복사본을 새로 만들어 내는 전략을 취한다. 만약 CopyOnWriteArrayList에서 Iterator를 뽑아내 사용한다면 Iterator를 뽑아내는 시점의 컬렉션 데이터를 기준으로 반복하며, 반복하는 동안 컬렉션에 추가되거나 삭제되는 내용은 반복문과 상관 없는 복사본을 대상으로 반영하기 때문에 동시 사용성에 문제가 없다.


반복문에서 락을 걸어야 할 필요가 있기는 하지만, 반복할 대상 전체를 한번에 거는 대신 개별 항목마다 가시성을 확보하려는 목적으로 잠깐씩 락을 거는 정도면 충분하다.


변경할 때마다 복사하는 컬렉션에서 뽑아낸 Iterator를 사용할 때는 ConcurrentModificationException이 발생하지 않으며, 컬렉션에 어떤 변경 작업을 가한다 해도 Iterator를 뽑아내던 그 시점에 컬렉션에 들어 있던 데이터를 정확하게 활용할 수 있다.


물론 컬렉션의 데이터가 변경될 때마다 복사본을 만들어내기 때문에 성능의 측면에서 손해를 볼 수 있고, 특히나 컬렉션에 많은 양의 자료가 들어 있다면 손실이 클 수 있다. 따라서 변경할 때마다 복사하는 컬렉션은 변경 작업보다 반복문으로 읽어내는 일이 훨씬 빈번한 경우에 효과적이다.


'Java' 카테고리의 다른 글

decompile..  (0) 2015.11.10
Serializable, 직렬화 (2)  (0) 2012.08.30
Serializable, 직렬화 (1)  (0) 2012.08.30

+ Recent posts