2009-07-01 2 views
5

다음 클래스를 실행하면 ExecutionService가 종종 교착 상태가됩니다.HashMap 작업을 수행 할 때 ExecutorService가 교착 상태에 빠지는 이유는 무엇입니까?

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 


public class ExecutorTest { 
public static void main(final String[] args) throws InterruptedException { 
    final ExecutorService executor = Executors.newFixedThreadPool(10); 

    final HashMap<Object, Object> map = new HashMap<Object, Object>(); 
    final Collection<Callable<Object>> actions = new ArrayList<Callable<Object>>(); 
    int i = 0; 
    while (i++ < 1000) { 
     final Object o = new Object(); 
     actions.add(new Callable<Object>() { 
      public Object call() throws Exception { 
       map.put(o, o); 
       return null; 
      } 
     }); 
     actions.add(new Callable<Object>() { 
      public Object call() throws Exception { 
       map.put(new Object(), o); 
       return null; 
      } 
     }); 
     actions.add(new Callable<Object>() { 
      public Object call() throws Exception { 
       for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 
        iterator.next(); 
       } 
       return null; 
      } 
     }); 
    } 
    executor.invokeAll(actions); 
    System.exit(0); 
} 

} 

그렇다면 왜 이런 일이 발생합니까? 또는 더 나은 방법은 - 사용자 정의 추상 맵의 구현이 스레드로부터 안전함을 보장하는 테스트를 작성하려면 어떻게해야합니까? (일부 구현에는 여러 맵이 있고 다른 구현자는 캐시 구현에 대한 대리자 등)

일부 배경 : Windows에서 Java 1.6.0_04 및 1.6.0_07에서 발생합니다. 나는 문제가) sun.misc.Unsafe.park (에서 오는 것을 알고 :

  • 내 코어 2 듀오 2.4 기가 헤르츠 고출력 노트북에 문제를 재현하지만, 디버그에서 실행되지 동안 수
  • 내 코어 2에서 디버깅 할 수 있습니다 직장에서 쿼드는,하지만 난 RDP 위를 걸어, 그래서 아래 내일까지 에게

대부분의 답변을 스택 트레이스를 얻을의 HashMap의 비 스레드 안전에 대한 있습니다 할 수 없습니다,하지만 난 찾을 수 HashMap에 잠긴 스레드가 없습니다. ExecutionService 코드 (및 Unsafe.park())에 모두 들어 있습니다. 나는 내일 실을 면밀히 조사 할 것이다.

이 모든 것은 사용자 정의 추상 맵 구현이 스레드로부터 안전하지 않았기 때문에 모든 구현이 스레드로부터 안전함을 보장하기 위해 설정되었습니다. 본질적으로, 나는 ConcurrentHashMap에 대한 나의 이해가 정확히 내가 기대하는 바를 확실히하고 싶지만, ExecutionService가 이상하게 부족하다는 것을 발견했다. ...

+0

나의 현재 행동 : - 그들은 크기를 조정하고 종료됩니다 스레드 밀접하게 1.이 HashMap의 중지 코드 당신은 어떤 스레드가 크기 조정 중지받지 않습니다 최신 VM – Stephen

+0

에 2. 업그레이드 [크기 조정]. Callable을 반복하는 스레드가 있는지 확인하십시오. –

답변

16

잘 알려진 스레드 안전하지 않은 교착 상태에 대해 불평하고있다. 나는 그 문제가 무엇인지 여기에서 알지 못한다. 또한

어떻게 ExecutionService

strangely lacking 

입니까?

을 사용하면 일반적인 오해입니다.HashMap 대부분 부실 데이터가 발생합니다. JVM을 터뜨릴 수있는 방법은 a beautiful race condition을 참조하십시오.

왜 이런 일이 발생하는지 이해하는 것은 매우 까다로운 과정이며 JVM과 클래스 라이브러리의 내부에 대한 지식이 필요합니다.

ConcurrentHashMap의 경우 javadoc을 읽으십시오. 질문이 명확해야합니다. 그렇지 않다면 Java Concurrency in Practice을보십시오.


업데이트 :

내가 당신의 상황을 재현하는 데 성공하지만 교착 아니다. actions 중 하나가 실행을 완료하지 않습니다. 스택 추적 :

"pool-1-thread-3" prio=10 tid=0x08110000 nid=0x22f8 runnable [0x805b0000] 
java.lang.Thread.State: RUNNABLE 
at ExecutorTest$3.call(ExecutorTest.java:36) 
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) 
at java.util.concurrent.FutureTask.run(FutureTask.java:138) 
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
at java.lang.Thread.run(Thread.java:619) 

그것은 내가에 연결된 정확한 경우처럼 보이는 - HashMap의 크기가 조절됩니다 인해 반복자의 크기를 조정의 내부 역학에 무한 루프에 갇혀됩니다.

이 경우 invokeAll이 반환되지 않고 프로그램이 중단됩니다.하지만 교착 상태도 아니고 라이브 록도 아니지만 경쟁 조건입니다.

+0

나는 그것을 얻는다 (나는 생각한다 - 나는 경쟁 조건에 대해 읽을 것이다). 문제는 실제로 HashMap 코드에 잠긴 스레드가 없다는 것입니다. 그것은 모두 ExecutionService 대기열 기능이었습니다. 예 : Unsafe.park()가 명시된대로. – Stephen

+0

6u14를 사용하여 코어 2에 교착 상태가 발생하지 않도록 할 수 있습니다. 교착 상태가 남아 있는지 보려면 ConcurrentHashMap을 사용해보십시오. 하지만 여기서 교착 상태에 대한 이유는 없습니다. –

+0

@Stephen - 나는 그것을 재현 할 수 있었고 설명을 추가했습니다. –

2

교착 상태로 무엇을 이해합니까?

코드에 두 가지 문제가 있습니다. HashMap은 여러 스레드에서 동시에 사용되므로 무한 루프가 발생할 수 있습니다. 각 개별 작업이 동기화 된 경우에도 잠재적으로 기본 데이터 구조를 변경하는 동안 항목 집합을 반복합니다. hasNext/next은 원자가 아닐 수 있습니다.

최신 Synhronized Security Release (SSH)의 1.6.0 버전은 1.6.0_13 및 1.6.0_14입니다. 테스트 작업을 측면에서

+0

SSR 릴리즈에 포함 된 내용에 대한 세부 정보 링크를 제공 할 수 있습니까? – pjp

+0

@pjp 그들은 특별한 CPU (Critical Patch Updates, 현재 Oracle Quarterly CPU와 동기화되지 않았기 때문에 특별)로 알려져 있습니다. 다음은 작성 당시 위험 매트릭스가 포함 된 최신 권고에 대한 링크입니다. http://www.oracle.com/technetwork/topics/security/javacpujune2011-313339.html#AppendixJAVA 더 자세한 정보는 일반적으로 제공되지 않습니다. –

0

- 대신 :

executor.invokeAll(actions); 

사용도

executor.invokeAll(actions, 2, TimeUnit.SECONDS); 

테스트가 실제로 작동 (그리고 오류를보고) 할 점에 유의, 당신은해야합니다

List<Future> results = executor.invokeAll(actions, 2, TimeUnit.SECONDS); 
executor.shutdown(); 
for (Future result : results) { 
    result.get(); // This will report the exceptions encountered when executing the action ... the ConcurrentModificationException I wanted in this case (or CancellationException in the case of a time out) 
} 
//If we get here, the test is successful... 
1

귀하의지도가 동시에 수정되고 있다고 생각됩니다. iterating 작업이 진행되는 동안 put()이 호출되면 특정 조건 (특히 크기가 변경되는 경우)에서 무한 루프가 발생할 수 있습니다. 이것은 꽤 잘 알려진 행동입니다 (here 참조).

교착 상태와 무한 루프는 매우 다르게 나타납니다. 진정한 교착 상태가 발생하면 스레드 덤프에 연동 스레드가 명확하게 표시됩니다. 반면 무한 루프에 빠지면 CPU가 급증 할 것이고 스택 추적은 덤프를 할 때마다 달라질 것입니다.

이것은 Executor와 관련이 없습니다. 안전하지 않은 동시 사용 HashMap은 그런 식으로 사용되지 않도록 설계되었습니다. 실제로 소수의 스레드 배열로이 문제를 재현하는 것은 꽤 쉽습니다.

가장 좋은 해결책은 ConcurrentHashMap으로 전환하는 것입니다. 동기화 된 HashMap 또는 Hashtable로 전환하면 무한 루프가되지 않지만 반복 중에는 ConcurrentModificationExceptions을 얻을 수 있습니다. 내일

관련 문제