2013-04-22 2 views
13

어떻게 작동하는지 간단한 설명이 필요 :는 연습 자바 동시성에 따르면, 장 11.4.3 말한다 "잠금 스트라이핑은"ConcurrentHashMap의

잠금 분할 때로는 독립적 인 객체의 variablesized 세트 잠금 파티션을 확장 할 수 있습니다 ,이 경우 잠금 스트라이핑이라고합니다. 예를 들어, 의 구현 ConcurrentHashMap은 해시 버킷의 1/16 을 보호하는 16 개의 잠금 배열을 사용합니다. 버킷 N은 잠금 N mod 16에 의해 보호됩니다.

잠금 스트라이핑 및 버킷 메커니즘을 이해하고 시각화하는 데 여전히 문제가 있습니다. 누군가 이해하기 좋은 단어로 설명 할 수 있습니까?

미리 감사드립니다.

답변

30

해시 맵은 해시 함수가 개체를 기본 배열의 요소에 매핑하는 배열에 구축됩니다. 기본 배열에 1024 개의 요소가 있다고 가정 해 봅시다. - ConcurrentHashMap은이를 실제로 64 개의 요소로 구성된 16 개의 다른 하위 배열로 변환합니다. {0, 63}, {64, 127} 등 각 하위 배열에는 자체 잠금이 있으므로 {0, 63} 하위 배열을 수정해도 {64, 127} 하위 배열에 영향을 미치지 않습니다. 다른 스레드가 두 번째 하위 배열에 쓰는 동안 첫 번째 하위 배열에 쓸 수 있습니다.

을 여러 스레드 자주 Collections.synchronizedMap()에 액세스 할 경우 각각의 방법 (즉 경우 공유 잠금을 사용하여 동기화되기 때문에 경쟁이 많이있을 것입니다, 다음과 같이

+0

오케이, 이해합니다. 하지만 두 개 이상의 스레드가 {0,63}의 하위 배열에서 모두 수정하려고하면 어떻게됩니까? – GedankenNebel

+3

그러면 처음으로 첫 번째로 제공됩니다. 잠금을 획득하는 첫 번째 스레드가 변경되며, 두 번째 스레드가 완료되면 두 번째 스레드가 변경됩니다. 'ConcurrentHashMap'는'replace'와 같은 메소드를 가지고있어서 두번째 쓰레드가 첫번째 쓰레드의 변경을 우연히 덮어 쓰지 않도록합니다. –

+0

나는 실제로 "선착순"이라고 생각하지 않는다. (나는 정확한 인용문을 가지고 있지 않지만 실제로 Java Concurrency in Practice에서 배웠다.) 공정성은 생성자에서와 같이 명시적일 때만 보장된다. 'ReentrantLock'이나'ArrayBlockingQueue'와 같은 대기열과 같은 다른 명시적인'Lock' 구현물에 대해서. (이전 스레드 인 것을 알고 있습니다. 미안합니다.) – Marcelo

11

Collections.synchronizedMap()에서 잠금의 차이와 ConcurrentHashMap이다 스레드 X가 Collections.synchronizedMap()에있는 메서드를 호출하면 스레드 X가 호출 한 메서드에서 반환 할 때까지 다른 모든 스레드는 Collections.synchronizedMap()에있는 메서드를 호출 할 수 없게됩니다.

ConcurrentHashMap에는 ConcurrentHashMap에있는 키 세그먼트를 보호하는 다양한 수의 잠금 장치 (기본값은 16)가 있습니다. 160 키를 가진 ConcurrentHashMap의 경우 각 잠금은 10 개의 요소를 보호합니다. 따라서 키 (get, put, set 등)에서 작동하는 메서드는 키가 동일한 세그먼트에있는 키에서 작동하는 다른 메서드에 대한 액세스 만 잠급니다. 예를 들어 스레드 X가 put(0, someObject)을 호출하고 스레드 Y가 put(10, someOtherObject)을 호출하면 해당 호출이 동시에 실행될 수 있으며 스레드 Y는 스레드 X가 put(0, someObject)에서 반환 될 때까지 기다릴 필요가 없습니다. 다음은 그 예입니다.

또한 size()isEmpty()과 같은 특정 방법은 전혀 지키지 않습니다. 이는 동시성을 높이는 반면, 일관성이 강하지 않다는 것을 의미합니다 (동시에 변경되는 상태는 반영하지 않습니다).

public static void main(String[] args) { 
    ConcurrentHashMap<Integer, Object> map = new ConcurrentHashMap<>(160); 

    new Thread(new Runnable() { 
    @Override 
    public void run() { 
     map.put(0, "guarded by one lock"); 
    } 
    }.start(); 

    new Thread(new Runnable() { 
    @Override 
    public void run() { 
     map.put(10, "guarded by another lock"); 
    } 
    }.start(); 

    new Thread(new Runnable() { 
    @Override 
    public void run() { 
     // could print 0, 1, or 2 
     System.out.println(map.count()); 
    } 
    }.start(); 
} 
+0

java의 HashMap은 스레드로부터 안전하지 않으며 HashTable입니다. 첫 번째 시나리오는 HashTable에 대한 것입니다. –

2

핵심 개념은 여기에 "버킷"입니다. 이 전체 해시 테이블에 전역 잠금을 사용하는 대신 각 버킷에 대해 하나의 작은 잠금을 사용합니다. 정렬 복잡도를 향상시킬 수있는 버킷 정렬과도 비슷합니다.

관련 문제