ConcurrentHashMap의 역할 및 사용법

ConcurrentHashMap의 역할 및 사용법

1.
ConcurrentHashMap 소개 ConcurrentHashMap은 JUC 툴킷의 동시 컨테이너 중 하나입니다.이 클래스는 다중 스레드 개발에서 자주 사용됩니다. HashMap과 HashMap의 차이점은 HashMap이 스레드로부터 안전하지 않다는 것입니다. 동시성이 높은 경우에는 다음을 사용하십시오. 많은 변경을 수행하는 HashMap은 문제가 발생하기 쉽지만 ConcurrentHashMap은 스레드로부터 안전합니다.
JDK1.8의 구현은 동시 업데이트의 안전성을 보장하기 위해 CAS + Synchronized를 사용하는 세그먼트 잠금 메커니즘과 채택 된 데이터 구조 (배열 + 연결 목록 + 빨강-검정 트리)를 포기했습니다.
ConcurrentHashMap은 차단되지 않도록 설계되었습니다. 데이터의 일부는 업데이트 중에 부분적으로 잠기지 만 전체 테이블은 잠기지 않습니다. 동기식 읽기 작업은 완전히 비 차단입니다. 장점은 합리적인 동기화를 보장한다는 전제하에 매우 효율적이라는 것입니다.

2. 일반적인 방법
1. Put 방법 : put 방법
에 대한 주석에주의하십시오. translate : "지정된 키를이 테이블의 지정된 값에 매핑하십시오. 키도 값도 비어있을 수 없습니다."따라서 키와 값은 ConcurrentHashMap의 HashMap과는 다른 널이 될 수 없습니다.

	/**
     * Maps the specified key to the specified value in this table.
     * Neither the key nor the value can be null.
     */
	public V put(K key, V value) {
    
    
        return putVal(key, value, false);
    }

    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
    
    
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode()); // 哈希算法
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
    
    //无限循环,确保插入成功
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0) //表为空或表长度为0时候
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
    
    
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)//MOVED=-1;当前正在扩容,一起进行扩容操作
                tab = helpTransfer(tab, f);
            else {
    
    
                V oldVal = null;
                synchronized (f) {
    
    // 同步加锁其它的操作
                    if (tabAt(tab, i) == f) {
    
    
                        if (fh >= 0) {
    
    
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
    
    
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
    
    
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
    
    
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
    
    
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
    
    
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
    
    
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

2. 작업 가져
오기 : 지정된 키가 매핑 된 값을 반환하거나 매핑에 키 매핑이 포함되지 않은 경우
1. 먼저 테이블이 비어 있는지 확인하고 비어 있으면 null을 직접 반환합니다.
2. 키의 해시 값을 계산하고 지정된 테이블의 지정된 위치에서 노드 노드를 구하고 연결 목록 또는 트리 구조를 순회하여 해당 노드를 찾은 다음 값 값을 반환합니다.

	/**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     */
    public V get(Object key) {
    
    
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode()); // hash算法
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
    
    
            if ((eh = e.hash) == h) {
    
    
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
    
    
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

3. ConcurrentHashMap 사용 표시 : 실제 사용은 매우 간단합니다. 컬렉션으로 이해하면됩니다.

package com.demo.spring.test.baseThread.并发容器;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description:  并发容器
 * @Author: yangshilei
 */
public class ConcurrentHashMapDemo implements Runnable{
    
    

    private final static ConcurrentHashMap<String,String> map = new ConcurrentHashMap();

    private final static  CountDownLatch latch = new CountDownLatch(100);

    private String key;

    private String value;

    public ConcurrentHashMapDemo(String key,String value){
    
    
        this.key = key;
        this.value = value;
    }

    @Override
    public void run() {
    
    
        map.put(key,value);
        latch.countDown();
    }


    public static void main(String[] args) {
    
    
        ExecutorService pool = Executors.newFixedThreadPool(8);
        try {
    
    
            for(int i = 1;i<=100;i++){
    
    
                ConcurrentHashMapDemo demo = new ConcurrentHashMapDemo("key"+i,"value"+i);
                pool.execute(demo);
            }
            latch.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            pool.shutdown();
        }
        System.out.println("map中的属性数量="+map.size());
    }
}

여러 실행의 결과 :

map中的属性数量=100


4. 실제 프로젝트의 사용 사례 : 실제 프로덕션 환경에서 코드에 대한 많은 작업만으로는 멀티 스레딩을 달성하기 위해 자주 말하는 방식을 사용하지 않지만 특정 사용 시나리오와 결합하면 인터페이스 또는 메서드가 반복적으로 수행됩니다. 시작하면 하나의 요청으로 새 스레드가 열립니다. 장면은 멀티 스레딩을 직접 사용하는 효과와 유사합니다.
멀티 스레딩의 경우 hashmap을 사용하면 연결된 목록의 폐 루프가 발생하며, 폐 루프가 데이터를 가져 오면 프로그램은 무한 루프에 들어가므로 HashMap은 스레드로부터 안전하지 않습니다.
따라서 이러한 시나리오에서는 ConcurrentHashMap도 사용해야합니다.
1. webSocket은 클라이언트 정보를 저장하는 데 사용됩니다
여기에 사진 설명 삽입
. 2. Redis 연결 풀 사용량
여기에 사진 설명 삽입
3. 다중 스레드 작업 관리
여기에 사진 설명 삽입

추천

출처blog.csdn.net/qq_37488998/article/details/109715821