3

다음은 몇 가지 의사 코드입니다.동시 수정 예외를 방지하는 가장 좋은 방법

public class MyObject 
{ 
    private List<Object> someStuff; 
    private Timer timer; 

    public MyObject() 
    { 
     someStuff = new ArrayList<Object>(); 

     timer = new Timer(new TimerTask(){ 

      public void run() 
      { 
       for(Object o : someStuff) 
       { 
        //do some more stuff involving add and removes possibly 
       } 
      } 
     }, 0, 60*1000); 
    } 

    public List<Object> getSomeStuff() 
    { 
     return this.someStuff; 
    } 
} 

그래서 본질적 문제는 코드에없는 다른 개체 위의 읽기 전용 목적을 위해 목록을 가져올 수() getSomeStuff를 호출 할 것입니다. 이 경우 타이머 스레드에서 concurrentmodificationexception이 발생합니다. getSomeStuff 메서드를 동기화하려고 시도하고 심지어 타이머 스레드에서 동기화 된 블록을 시도했지만 여전히 오류가 계속 발생했습니다. 목록의 동시 액세스를 중지하는 가장 쉬운 방법은 무엇입니까?

답변

11

스레드에서 목록을 반복하기 전에 java.util.concurrent.CopyOnWriteArrayList을 사용하거나 복사 (또는 Collection.toArray 메서드로 배열 가져 오기) 할 수 있습니다.

그 외에도 for-each 구문을 제거하면 반복기가 손상되므로이 경우 목록을 처리하는 올바른 방법이 아닙니다.

하지만 당신이 할 수있는 다음 :

for (Iterator<SomeClass> i = list.iterator(); i.hasNext();) { 
    SomeClass next = i.next(); 
    if (need_to_remove){ 
     i.remove(i);     
    } 
} 

또는

for (int i = list.size() - 1; i >= 0; i--){    
    if (need_to_remove) { 
     list.remove(i);     
    } 
} 

은 또한 코드가 다른 스레드의 목록을 액세스하고 목록이 변경되었을 경우, 당신이 그것을 동기화해야한다는주의 . 예를 들면 다음과 같습니다.

private final ReadWriteLock lock = new ReentrantReadWriteLock(); 


    final Lock w = lock.writeLock(); 
    w.lock(); 
    try { 
     // modifications of the list 
    } finally { 
     w.unlock(); 
    } 

     ................................. 

    final Lock r = lock.readLock(); 
    r.lock(); 
    try { 
     // read-only operations on the list 
     // e.g. copy it to an array 
    } finally { 
     r.unlock(); 
    } 
    // and iterate outside the lock 

그러나 잠금이 가능한 작업은 가능한 짧아야합니다.

+0

그러나 스레드 (추가 및 제거)에서 수행 된 작업은 실제로 목록 개체에서 유지되어야합니다. 다른 스레드가 읽기 전용 (foreach 루프)으로 목록에 액세스하여 JVM을 괴롭히는 것을 원하지 않습니다. – thatidiotguy

+0

확인, 목록에 대한 삭제/반복에 관한 업데이트를 참조하십시오. –

+0

자바의 동기화 키워드로 뭔가를하는 것이 가능했을 것이라고 생각하지만, 그렇지 않다고 말하는 것입니까? 그리고 이러한 Lock 및 ReadWriteLock 패키지는 무엇입니까? 또한 타이머의 run 메서드가 실행되는 동안 목록 자체를 잠글 필요가 있습니다.이 방법을 사용하는 것이 좋습니다? – thatidiotguy

3

getSomeStuff()에 목록 사본을 만들어야합니다. 이와 같이 비공개 필드에 대한 참조를 퍼블리시하면 효과적으로 퍼블릭하게되므로 어쨌든 원하는 것은 아닙니다.

또한 사본을 ImmutableList 또는 적어도 unmodifiable list으로 반송하는 것이 좋습니다.

+0

현재 내가하고있는 일은 Collections.synchronizedList를 반환하는 것입니다 (작동하지 않습니다). 목록을받는 객체는 읽기 용도로 반복 할 수 있어야하지만 그게 전부입니다. 이 중 하나가 동시 수정 예외 문제를 해결할 수 있습니까? 개체 보안에 대한 요점. – thatidiotguy

관련 문제