ConcurrentHashMap 탐구

3992 단어 자바
ConcurrentHashMap
Concurrent HashMap 은 스 레 드 가 안전 하고 성능 이 좋 은 Map 의 스 레 드 안전 실현 입 니 다.HashMap 에 비해 스 레 드 가 안전 합 니 다.HashTable 의 성능 비교 우 위 는 매우 뚜렷 합 니 다.그의 사용 은 매우 간단 하 다.여 기 는 주로 Concurrent HashMap 의 실현 원 리 를 탐구 하고 자 한다.여기 서 모두 알 아야 할 문제 가 있다.
  • Concurrent HashMap 은 왜 HashTable 보다 성능 이 높 습 니까?
  • Concurrent HashMap 은 JDK 8 과 JDK 7 에 어떤 변화 가 있 습 니까?왜 이런 변화 가 있 습 니까?우리 개발 에 어떤 시사 점 이 있 습 니까?
  • 은 왜 JDK 8 에서 Synchronized 를 사용 합 니까?ReentrantLock 이 아 닌 Synchronized 를 사용 합 니까?

  • 이 몇 가지 문 제 를 가지 고 Concurrent Hash Map 의 소스 코드 를 분석 해 보 겠 습 니 다.
    ConcurrentHashMap 정의
    JDK 8(JDK 7 도 마찬가지)에서 Concurrent HashMap 의 정 의 는 다음 과 같다.
    public class ConcurrentHashMap extends AbstractMap
        implements ConcurrentMap, Serializable {

    Concurrent HashMap 의 JDK 7 에서 의 실현
    자바 7 에서 Concurrent HashMap 의 실현 은 세그먼트 잠 금 을 바탕 으로 이 루어 졌 다.그의 바 텀 데이터 구 조 는 아직도 배열+링크 로 Hash Table 과 달리 Concurrent Hash Map 의 가장 바깥쪽 은 큰 배열 이 아니 라 Segment 배열(세그먼트 잠 금 의 실현)이다.세그먼트 자 물 쇠 는 자물쇠 의 입 도 를 줄 여 병발 정 도 를 높 였 다.해시 테이블 보다 효율 성 이 높 은 이유 다.HashTable 의 소스 코드 는 매우 간단 합 니 다.HashTable 과 HashMap 의 구조 가 일치 하지만 모든 방법 은 Synchronized 로 수식 하여 작업 이 안전 하도록 합 니 다.이렇게 다 중 스 레 드 의 경우 잠 금 동작 hashTable 의 데 이 터 를 가 져 오 는 스 레 드 만 있 습 니 다.Courrent HashMap 은 아니 며,최대 segment 배열 길이 의 스 레 드 가 Concurrent HashMap 의 데 이 터 를 동시에 조작 할 수 있 습 니 다.
    Concurrent HashMap 의 전체 구 조 는 다음 과 같다(그림 출처:http://www.jasongj.com/java/c...):
    Concurrent HashMap 의 정의:
    public class ConcurrentHashMap extends AbstractMap
            implements ConcurrentMap, Serializable {
        private static final long serialVersionUID = 7249069246763182397L;
    
        /**
         *       
         */
        static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        /**
         *       
         */
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
        /**
         * segments       ,                segments     ,    segments      2 N  
         */
        static final int DEFAULT_CONCURRENCY_LEVEL = 16;
    
        /**
         * HashEntry    
         */
        static final int MAXIMUM_CAPACITY = 1 << 30;
    
        /**
         * segment     
         */
        static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
    
        /**
         * segment     
         */
        static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
    
        /**
         *     ,          
         */
        static final int RETRIES_BEFORE_LOCK = 2;
    
        /**
         *        ,  ssize-1
         */
        final int segmentMask;
    
        /**
         *            ,  32-sshift
         */
        final int segmentShift;
    
        /**
         *   segment  
         */
        final Segment[] segments;

    세그먼트 정의:
    static final class Segment extends ReentrantLock implements Serializable {
        transient volatile HashEntry[] table;
        transient int count;
        transient int modCount;
        //   ,       *    ,           ,    
        transient int threshold;
        //segment hashEntry     
        final float loadFactor;
    
    }
  • get()작업 은 두 번 의 해시 를 통 해 HashEntry 를 찾 은 다음 에 옮 겨 다 니 는 작업 을 합 니 다.get 방법 에서 사용 하 는 변 수 는 모두 volatile 형식 입 니 다.스 레 드 를 볼 수 있 고 다 중 스 레 드 에 의 해 동시에 읽 을 수 있 으 며 만 료 된 값 을 읽 지 않도록 합 니 다.get 작업 에서 count 와 value 두 개의 공유 변 수 를 읽 어야 하기 때문에 잠 금 을 추가 할 필요 가 없습니다.volatile 필드 의 기록 작업 은 읽 기 작업 보다 먼저 이 루어 집 니 다.모든 스 레 드 가 수정 되 더 라 도 get 은 최신 값 을 얻 을 수 있 습 니 다.
  • put()는 변수의 hashCode 를 한 번 에 해시 한 다음 에 Segment 로 위치 한 다음 에 Segment 에 삽입 작업 을 합 니 다.HashEntry 배열 이 threshold 를 초과 하면 확장,확장 은 Segment 에 대한 확장 일 뿐 입 니 다.
  • size()는 Concurrent HashMap 에서 요소 의 개 수 를 통계 하려 면 Segment 의 모든 요 소 를 구 해 야 합 니 다.Segment 에서 전체 변 수 는 volatile 형식의 변수 입 니 다.누적 하면 요소 의 총 개 수 를 얻 을 수 있 지만 정확 하지 않 습 니 다.사용 한 count 를 나중에 바 꿀 수 있 기 때 문 입 니 다.마지막 방법 은 put,remove 를 막 는 것 입 니 다.clean 등 요 소 를 조작 하 는 방법 은 매우 비효 율 적 입 니 다.따라서 Concurrenthashmap 은 두 번 잠 그 지 않 고 segment 의 요소 크기 를 통계 하려 고 시도 합 니 다.두 번 의 결과 가 다 르 면 잠 금 을 추가 하 는 방식 으로 용기 의 변화 여 부 는 modCount 의 변화 여 부 를 통 해 확 정 됩 니 다.

  • Concurrent HashMap 의 JDK 8 에서 의 실현

    좋은 웹페이지 즐겨찾기