2016-10-14 4 views
-2

연결 목록이있는 서버 응용 프로그램이 있습니다. 이 프로그램은 여러 스레드를 사용합니다. 주 스레드는 간격을두고 프로세스를 실행하여 닫힌 연결을 감지 한 다음 배열에서 제거하여 가비지 수집 할 수 있도록합니다.HashMap에서 항목을 제거하면 ConcurrentModificationException이 발생합니다.

이 과정은 다음과 같다 :

private void cullOtherProcessors() throws Exception { 
    //log.log(Level.FINE,"XMLMessageReciever:cullOtherProcessors"); 
    List<ConnectionAppInterface> toDel = new ArrayList<ConnectionAppInterface>(); 
    for (ConnectionAppInterface cur : m_OtherProcessors.keySet()) { 
     if (cur!=null) { 
      if (cur.isClosed()) { 
       //Connection is closed - we could never send a message over it 
       toDel.add(cur); 
      } 
     } 
    } 
    for (int c=0;c<toDel.size();c++) { 
     log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - removing closed connection"); 
     m_OtherProcessors.remove(toDel.get(c)); 
    } 
} 

내 서버가 몇 달에 출마하지만 다음과 같은 오류와 함께 추락 로그에 따라 :

기본적으로
08/10/16 01:06:39  calling connect 
08/10/16 01:06:39 **XMLMessageReciever:cullOtherProcessors - removing closed connection 
08/10/16 01:06:39 CloseableThread: End of run for thread Socket.Connect(113726) 
08/10/16 01:06:39  Checking connected 
08/10/16 01:06:39  Active Threads this group: 5 
08/10/16 01:06:39  getting Message Reciever 
08/10/16 01:06:39  Active Threads: 8 
08/10/16 01:06:39  Setting m_establishingCon 
08/10/16 01:06:39  Establishing connection to robertsNode 
Server Failed 
java.util.ConcurrentModificationException 
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) 
    at java.util.HashMap$KeyIterator.next(HashMap.java:1453) 
    at metcarob.com.common.network.xmlprotocol.XMLMessageReciever.cullOtherProcessors(XMLMessageReciever.java:57) 
    at metcarob.com.common.network.xmlprotocol.XMLMessageReciever.LoopIteration(XMLMessageReciever.java:98) 
    at metcarob.com.common.network.xmlprotocol.ConnectionManager.LoopIteration(ConnectionManager.java:48) 
    at metcarob.com.personalservices.singlenodeserver.Main.run(Main.java:138) 
    at metcarob.com.personalservices.singlenodeserver.Main.main(Main.java:398) 

뭔가 ArrayList에 일어난 nextNode가 예외를 던지게하면서 (아마도 다른 연결이 설정되어) 반복하면서 루프를 돌고있었습니다.

나는 주변을 둘러 볼 수있는 가장 좋은 방법을 찾으려고 노력 중이다. 나는 단순히 오류를 포착하고 무시하는 것을 고려하고있다. 누락 된 스레드는 단순히 다음 루프에서 제거됩니다. 제안 된 솔루션은 다음과 같습니다.

private void cullOtherProcessors() throws Exception { 
    //log.log(Level.FINE,"XMLMessageReciever:cullOtherProcessors"); 
    //m_OtherProcessors 
    List<ConnectionAppInterface> toDel = new ArrayList<ConnectionAppInterface>(); 
    try { 
     for (ConnectionAppInterface cur : m_OtherProcessors.keySet()) { 
      if (cur!=null) { 
       if (cur.isClosed()) { 
        //Connection is closed - we could never send a message over it 
        toDel.add(cur); 
       } 
      } 
     } 
    } catch (ConcurrentModificationException e) { 
     log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - ConcurrentModificationException being ignored"); 
    } 
    for (int c=0;c<toDel.size();c++) { 
     log.log(Level.FINE,"**XMLMessageReciever:cullOtherProcessors - removing closed connection"); 
     m_OtherProcessors.remove(toDel.get(c)); 
    } 
} 

이제이 코드를 다시 서버에 저장하고 더 오랫동안 실행합니다.

이 문제에 대한 좋은 해결책인지 알고 싶습니다. 처음부터이 문제가 발생하기까지 수개월이 걸렸고 5 분마다 코드가 연결을 반복하므로 연결이 자주 끊어지지 않을 확률이 적다는 생각이 듭니다. (로그를보고 볼 것입니다)

전문가가 생각하는 것이 좋을 것입니다.

** 그것은이 질문에 제안되었다

그것은 아니다 "루프에서 제거 할 때, 컬렉션을 반복하는 ConcurrentModificationException를 피"와 동일합니다. 내 코드에서는 HashMap을 반복하면서 항목을 제거하지 않고 대신 맵을 반복하고 제거 할 항목을 임시 Array에 저장합니다. 그런 다음 배열을 통해 항목을 제거합니다.

+0

어머, 예외를 무시하는 것은 쉬운 일이 아닙니다. 이 장치를 추출하고 스레드로 문제를 재현하려 했습니까? 당신이 동일한 데이터 구조를 읽고 쓰고 있기 때문에 나는 경쟁 조건을 보았습니다. syncronisation없이 m_OtherProcessors를 – Paolof76

+0

m_OtherProcessors를 생성하는 곳은 어디입니까 ?? –

+0

오류가 멀티 스레딩과 관련이 있습니까? ConcurrentModificationException은 스레드와 관련이 없습니다. 다중 스레딩으로 인해 발생하는 경우 오류를 잡아도 동시 데이터 구조를 사용하지 않아도되는 결과를 피할 수는 없습니다. –

답변

0
+0

링크를 제공해 주셔서 감사합니다. 나는 synchronizedMap을 조사하고있다. (나는 m_OtherProcessors를 synchronizedMap으로 바꿀 수도있다) – Robert3452

+0

당신은 여전히리스트를 반복적으로 반복하기 위해 내장 락 (intrinsic lock)을 사용할 필요가있다. –

+0

리스트는 메소드 호출에 엄격히 국한되어있다. thread의 안전성에 관한 한, 문제는 없습니다. 스레드 안전 구현을 사용하면 도움이되지 않습니다. 스택 추적은 맵의 키 집합에 대한 반복입니다. -1. – GPI

0

안녕 보인다 위에 반복하는 동안 m_OtherProcessors의 수정을해야합니다.

내가 생각할 수있는 예외를 포착하는 것보다 더 좋은 해결책이 있습니다.

1 - m_OtherProcessors가 작은 개체이고 mem이 문제가 아닌 경우 HashMap < ...> copy = new HashMap < ..> (m_OtherProcessors)와 같은 개체의 로컬 복사본을 만들면됩니다. 그리고이 객체로 함수를 실행하십시오. (복사 중에 크래시가 발생할 가능성을 피하려면 동기화되기 전에 동기화 명령을 삽입해야합니다. (HashMap < ...> copy = new HashMap <> (m_OtherProcessors)})

2 - 방금 메서드 또는 arrayList를 동기화하면 이러한 동기화로 인해 성능이 저하되지만 안정성이 향상됩니다.

+0

해결책 1은 작동하지 않습니다 : hashMap을 새로운 것으로 복사하는 것은 항목을 통해 반복을 수반합니다. 이 반복은 포스터가 직면 한 문제를 일으키는 것보다 안전하지는 않지만, 소리는 거의 발생하지 않습니다. OP의 코드와 함께 발생하면 루프도 나타나고 동일하게 수행됩니다. 해결 방법 2는 작동하지 않습니다. 목록의 반복에는 아무런 문제가 없습니다.이 방법은 엄격하게 메서드에 대해 로컬입니다. -1. – GPI

+0

해결 방법 1은 새 개체 만들기 작업이 작아서 같은 개체가 만들어 질 때 수정되지 않도록합니다.그러나 내가 말했듯이 (적은 기회로)이 오류가 발생하여 객체 작성시 statment를 추가하면 문제가 해결됩니다. 두 번째 부분에서는 m_OtherProcessors.keySet()을 통해 목록의 iteration이 완전히 잘못되었습니다. iterator가 필요하지 않으므로 제안한대로 로컬이 아닙니다. –

+0

Sol. 1 : 여기서 syncrhonizing 충분하지 않습니다, 당신은 너무 수정을 수행하는 측면에서 동기화 제안해야합니다. Sol. 2 : 나는 당신의 제안을 읽으면서 arrayList를 sychronizing하는 것이 keySet 반복을 더 안전하게하는 방법을 보지 못했다. ArrayList는 완벽하게 안전합니다. 메서드 내에서 인스턴스화되고 참조되며 코드에서 반복되는 동안 수정되지 않습니다. 지도 키 집합이 문제입니다. 예외가 간다면 arrayList가 없어도 여전히 문제가 발생할 수 있습니다. 그것은 단순히 관련이 없습니다. – GPI

0

동시 이동 예외는 사용자가 컬렉션을 탐색/읽는 중 데이터 수정을 수행 할 때 발생합니다.

가장 쉬운 방법은 다음과 같습니다. collection을 트래버스하려면 iterator을 사용하십시오.

ex.

Iterator<String> iter = myArrayList.iterator(); 

    while (iter.hasNext()) { 
     String str = iter.next(); 

     if (someCondition) 
      iter.remove(); 
    } 
+0

사실이지만 불완전합니다. 다중 쓰레드 시나리오에서 이것은 도움이되지 않을 것입니다 : hashmap의 파생 iterator는 동시 액세스의 경우 제거에 안전하지 않습니다. 낡은 데이터, 보이지 않는 데이터, 무한 루프 내에서 코드를 차단할 수 있습니다 ... 모든 종류의 나쁜 일이 발생할 수 있습니다. 또한 stacktrace는이 문제가 목록의 요소 제거 내부에 있지만 hashmap의 반복 내부에 있음을 언급하지 않습니다. 그러므로,이 답변은, 진실을 말하고는 있지만,이 질문에는 부적합합니다. -1. – GPI

+0

다중 스레드 시나리오에서 OP는 동기화 된 블록 또는 잠금 메커니즘을 사용할 메커니즘에 따라 프로젝트에 전적으로 의존합니다. –

+0

예, OP는 여러 스레드에서 동시에 발생하는 수정 예외가 있습니다. 이는 스택 추적에 의해 명확 해지기 때문에 게시물이 올바르게 주소 지정하는 단일 스레드 문제에서 동시 제거가 아닌 유효한 응답이됩니다. 문제. – GPI

1

HashMap 당신이 그렇지 않으면 당신은 하드 버그 ConcurrentModificationException처럼 또는 memory barriers로 인해 재현하는 얻을 것이다 여러 스레드와 공유 할 생각하지 않는 것을 의미한다 하지 스레드 안전 모음입니다.

잡기 ConcurrentModificationException을 잡는 것은 나쁜 습관이므로지도가 동시에 수정되어 깨진지도 및/또는지도로 이어질 수있는 경우 알려주는 HashMap의 경비로 여겨 져야하므로 절대 수행하지 않아야합니다. 데이터 손실이 더 심합니다.

java.util.concurrent 패키지의 스레드로부터 안전한 컬렉션을 사용해야합니다. 스레드 안전 Map이 필요한 경우 가장 좋은 방법은 ConcurrentHashMap입니다.

+1

이 게시물에 동의하지만 목록의 스레드 안전성에 대해 언급하는 이유는 무엇입니까? 여기에있는 문제 (stacktrace)는지도의 키 집합 반복에 대한 스레드 안전성과 관련이 있습니다. 여기에서 반복되는 유일한리스트는 완전히 안전합니다 (메소드 호출에 국한되어 있습니다). 아무런 문제도 없습니다. 그래서 나는 동의한다. 그러나 이것은 나에게 "요점 옆에있는"것처럼 보인다? – GPI

+0

@GPI 당신 말이 맞아요, 나는 지금 당장 고쳐진 질문에 혼란스러워했습니다. –

+0

@GPI 나는 또한 내가 혼란 스러웠던 이유에 대한 질문의 제목을 수정했습니다. 귀하의 발언에 대해 –

관련 문제