왜 HashMap의 스레드 안전?

소개 : 우리의 HashMap는 스레드로부터 안전하지 않습니다하는 다중 스레드 환경에서 모두 알고하지 않는 것이 좋습니다,하지만 주로 안전한 장소에게 그것을 스레드 무엇에 반영이 문서는 문제를 해독합니다.

1.jdk1.7되는 HashMap

jdk1.8에서의 HashMap은에서 jdk1.7에서 문제의 첫 번째 분석은, 나는 우리가 우리가 처음 죽은 표시 시뮬레이션 코드를 사용하여 다중 스레드 환경에서 모든 노하우되는 HashMap 경향이 jdk1.7의 무한 루프 생각 최적화의 많은 일을 사이클 상황 :

public class HashMapTest {

    public static void main(String[] args) {
         HashMapThread thread0 = new HashMapThread();
         HashMapThread thread1 = new HashMapThread();
         HashMapThread thread2 = new HashMapThread();
         HashMapThread thread3 = new HashMapThread();
         HashMapThread thread4 = new HashMapThread();
         thread0.start();
         thread1.start();
         thread2.start();
         thread3.start();
         thread4.start();
     }
}

class HashMapThread extends Thread {
    private static AtomicInteger ai = new AtomicInteger();
    private static Map<Integer, Integer> map = new HashMap<>();

    @Override
    public void run() {
        while (ai.get() < 1000000) {
             map.put(ai.get(), ai.get());
             ai.incrementAndGet();
         }
     }
}

코드는 비교적 간단한 조작을 넣어 연속적인 복수의 스레드를 열고, 그리고 AtomicInteger와의 HashMap 세계적으로 공유된다. 실행 여러 번에 코드가 표시보다 더 같은 무한 루프 시나리오를 다음과 같습니다 :

국경을 여러 번의 배열이 나타납니다되는 경우가 있습니다 :

여기에 우리가 무한 루프의 경우이 나타납니다 이유를 분석, 명명 된 뷰에 죽음과 표준 새 번역 jstack을의주기를 통해 상황을 집중, 결과는 다음과 같습니다 :

정보가 정보가 HashMap의 확장 기능에서 발생한 죽음의주기 명확하게 인식 할 수있는 스택 위치 무한 루프에서 볼 수있는,의 루트 전달 함수 HashMap의 전달 함수에서의, jdk1.7은 다음과 같습니다

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

함수의 주요 역할을 요약한다 :

newTable의 팽창이 전사 요소의 과정에서 알 수있는 일본어 newTable로 데이터를 전송할 필요 통지 라인 10-12 용 테이블에서 제 보간법 즉 될리스트의 순서를 반대로 이용된다 여기에서 중요한 점은 무한 루프를 형성하는 것입니다. 아래의 상세 분석.

무한 루프 분석 결과 1.1 확장

전제 조건 :

그것은 가정한다

# 키 모드 목록 크기 1.hash의 간단한 알고리즘.

# 2는 초기 해시 테이블 크기 = 2 키 = 3,7,5은의 [표 1]에있다.

# 3. 그런 다음 크기 때문에 4로, 크기를 조정합니다.

크기 조정 이전에 어떤 데이터 구조는 다음과 없습니다 :

단일 스레드 환경의 경우, 최종 결과는 다음과 같습니다 :

전송 과정은 여기, 당신이 반전 목록에 전송 프로세스 및 방법은 어렵지 않을해야하는 전송 기능을 이해하고 나면 무엇을해야 하는지를 상세히 설명되지.

그리고 다중 스레드 환경에서, 두 개의 스레드 A와 B가 넣어 작업 중에이 있다고 가정합니다. 분석 위치에 함수가 여기에 매우 중요하기 때문에 거는 코드의 첫 번째 11 개 라인의 전달 함수의 구현에 스레드, 그래서 다시 기록했다.

다음과 같이이 경우 결과에 스레드는 다음과 같습니다

부유 후, 스레드 A, B가 다음 정상적으로 실행 스레드 및 크기 조정 작업이 완료되면, 결과는 다음이다 :

스레드 B가 구현 되었기 때문에 자바 메모리 모델에 따라, 현재는 메인 메모리에서 newTable 항목 표는 최신 값은 다음과 같습니다 :이 점에 특히주의를 기울여야한다 7.next = 3,3.next = null를 돌려줍니다.

이 시점에서, 스레드 A로 전환 메모리 정지 된 스레드의 값은 다음과 같다 : 다음 E = 3 = 7, newTable를 [3] = NULL, 코드 실행 과정은 다음된다 :

newTable[3]=e ----> newTable[3]=3
e=next ----> e=7

다음과 같은 결과는이 경우 :

사이클을 계속 :

e=7
next=e.next ----> next=3【从主存中取值】
e.next=newTable[3] ----> e.next=3【从主存中取值】
newTable[3]=e ----> newTable[3]=7
e=next ----> e=3

결과는 다음과 같다 :

다시 순환 :

e=3
next=e.next ----> next=null
e.next=newTable[3] ----> e.next=7 即:3.next=7
newTable[3]=e ----> newTable[3]=3
e=next ----> e=null

e.next = 7, 마지막 사이클 7.next = 3 원형 링크 목록을 발생하고,이 시간 (E)에서 = NULL 사이클 끝난다주기가 있습니다.

결과는 다음과 같다 :

지금까지 폴링 해시 맵의 데이터 구조뿐 무한 루프 비극, 후속 작업에서 여기가 발생한다.

데이터 분석 과정의 손실을 초래 1.2 확장

처음에, 위의 분석에 따라 :

스레드 및 스레드 B 넣어 조작 보류 동일한 스레드 A이다 :

시 실행 스레드는 결과로 다음과 같습니다 :

이 때, 스레드 B는 더 CPU 시간 조각을받지 및 크기 조정 작업을 완료했다 :

: 인해 종료 스레드 B의 실행에 newTable 테이블 최신 값 참고 5.next = NULL .

이 시점에서, 스레드 A로 전환 스레드 중단 A : E = 7 = 5이어서, NewTable = NULL.. (3).].

newtable [I] = E ** 7 테이블에 배치한다 [3] ** 위치 다음 = 5 수행. 그런 다음주기 :

e=5
next=e.next ----> next=null,从主存中取值
e.next=newTable[1] ----> e.next=5,从主存中取值
newTable[1]=e ----> newTable[1]=5
e=next ----> e=null

(5)가 테이블에 저장된다 [1] E = NULL 사이클이 종료 위치, 3 손실 소자 및 형성되는 원형 링크드리스트 . 그리고 후속 작업의 해시 맵 중 무한 루프가 발생합니다.

2.jdk1.8 中의 HashMap

최적화의 HashMap에 jdk1.8에서 충돌 상황이 여전히 멀티 스레드의 경우 안전하지 않은 원형 연결리스트를 표시하지 않지만, 그래서 해시, 더 이상 처음 보간 모드를 사용하지만 직접 목록의 꼬리에 발생 여기에 우리가 작업 jdk1.8 소스의 HashMap에 넣어를 참조하십시오

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null) // 如果没有hash碰撞则直接插入元素
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; 
        K k;
    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
    else if (p instanceof TreeNode)
        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    else {
        for (int binCount = 0; ; ++binCount) {
            if ((e = p.next) == null) {
                p.next = newNode(hash, key, value, null);
                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null)
            e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

이것은 HashMap의 jdk1.8의 주요 기능은 동작이있는 경우, 코드 라인 (6)은, 어떠한 해시 충돌 요소로 직접 발생하지 않도록주의를 넣고있다. 스레드 A 및 스레드 B가 동작을 넣을 경우 단지 해시 값과 두 개의 서로 다른 데이터 및 상기 위치 데이터는 스레드 A는 B 코드 라인 (6)을 입력한다 그래서 널된다. 상황을 가정, 스레드 A가 보류 데이터 삽입이 수행되지 않았으며, 스레드 B는 통상의 데이터를 삽입하기 위해 일반적으로 실행되고, CPU 시간 슬라이스 획득 스레드 A는 다음 문제, 더 판정 해시 스레드 없다 다음에 데이터를 삽입하는 스레드 B가 스레드 커버 , 스레드 안전의 발생.

여기에 대한 간략한 분석 낮은 jdk1.8은 HashMap의 스레드 불안에 나타납니다 반영하고, 특정 분석 한 후 프레임 워크가 요약되어 자바 컬렉션에-후속 것이다.

개요

최초의 HashMap는 스레드 안전하지 주로 반영 :

팽창시 jdk1.7에서 # 1은 다중 스레드 환경에서는 엔드리스 체인 또는 데이터 손실이 발생할 것이다.

jdk1.8에서 # 2는 멀티 스레드 환경에서 데이터의 경우 커버리지가 열린다.

게시 50 개 원래 기사 · 원 찬양 1706 · 조회수 2,220,000 +

추천

출처blog.csdn.net/zl1zl2zl3/article/details/105233273