2014-09-23 2 views
14

ConcurrentHashMap (put(), remove() 등)에서의 비회원 작업은 모두 synchronized(this) 블록으로 묶어야합니까? 나는이 모든 작업이 스레드로부터 안전하다는 것을 이해하고 있으므로 그렇게 할 때 실질적인 이익/필요성이 있습니까? 사용 된 유일한 작업은 put()remove()입니다.ConcurrentHashMap을 동기화 된 블록에 래핑해야합니까?

protected final Map<String, String> mapDataStore = new ConcurrentHashMap<String, String>(); 

public void updateDataStore(final String key, final String value) { 
    ... 
    synchronized (this) { 
     mapDataStore.put(key, value); 
    } 
    ... 
} 

답변

35

아니요, 그렇게하면 ConcurrentHashMap의 혜택을 잃게됩니다. 당신은뿐만 아니라 전체 테이블을 잠그는 synchronized 또는 synchronizedMap()HashMap를 사용 할 수있다 (synchronized에서 작업을 포장 할 때 암시 모니터가 전체 개체 인스턴스이기 때문에 당신이하는 일이다.)

ConcurrentHashMap의 목적에있다 전체 테이블을 잠그지 않고 테이블에서 동시 읽기/쓰기를 허용하여 동시 코드의 처리량을 증가시킵니다. 이 테이블은 잠금 스트라이핑 (각 잠금이 해시 버킷 집합에 할당 된 1 개가 아닌 여러 개의 잠금 - Goetz 외의 Java Concurrency in Practice 참조)을 사용하여 내부적으로이를 지원합니다.

ConcurrentHashMap을 사용하면 모든 표준지도 방법 (put(), remove() 등)은 구현시 잠금 줄무늬 등으로 인해 원자 적입니다. 유일한 단점은 size()isEmpty()과 같은 메소드가 전체 테이블을 잠그는 모든 작업에 필요한 유일한 방법이므로 정확한 결과를 반환하지 않을 수도 있다는 것입니다.

ConcurrentMap interface 인터페이스는 키와 값을 모두 수용, (키가지도에없는 만하면 뭔가를 넣어) putIfAbsent() 같은 remove() 새로운 원자 화합물 작업을 추가합니다 (그 값은 당신이 통과 매개 변수와 동일한 경우에만 항목을 제거), 등등.이 작업은 두 개의 메소드 호출 (예 : putIfAbsent()Map 표준을 사용하는 경우 synchronized 블록으로 둘러싸인 containsKey()put()의 호출을 필요로했기 때문에 전체 테이블을 잠그도록 요구했습니다.) 한 번 다시 전체 테이블 잠금을 피함으로써 이러한 방법을 사용하여 더 많은 처리량을 얻을 수 있습니다.

6

이러한 작업을 동기화해도 아무런 이점이 없습니다. 동기화가 필요하지 않으면 실제로 성능이 저하됩니다.

이유는 Collections.synchronizedMap(map)과 같은 동기화 된 맵 (질문에서와 같이 수동으로 구현되거나 일반적인 방법으로 인스턴스화 됨)이 많은 스레드에 의해 액세스 될 때 성능이 저하된다는 것입니다. put 및 get 작업이 블로킹되므로 다른 모든 스레드가 대기해야하며 동시에 맵에 액세스 할 수 없습니다. ConcurrentHashMap은 이름에서 알 수 있듯이 다른 한편으로는 동시 액세스를 허용합니다. 동기화를 추가하면이 이점이 손실됩니다.

관련 문제