본문 바로가기

Programming/Java

ConcurrentHashMap

스프링 회원 관리 예제 강의를 듣다가 ConcurrentHashMap에 대해 듣게 되었다

예제 부분에서는 그냥 Map HashMap을 사용했는데 ConcurrentHashMap은 현업에서 사용한다. 이렇게 들었던거 같다

 

그래서 궁금해져서 찾아 보았다 ConHashMap은 어떤 녀석인지

 

HashTable, HashMap, ConcurrentHashMap 이렇게 3종류가 있다

  • HashTable : sychronized가 존재한다. mutil-thread에서 사용 할 수 있다 단, 속도가 느리다. key, value에 null을 허용하지 않는다. 
  • HashMap : 대표적으로 사용하는 HashMap 애는 속도 빠르다 단, sychronized기능이 없기 때문에 mutil-thread상황에서는 사용하지 못한다. key, value에 null을 허용한다.

이러한 문제들을 해결하기 위해 있는 것이

ConcurrentHashMap이다.

 

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; K fk; V fv;
            if (tab == null || (n = tab.length) == 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)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else if (onlyIfAbsent // check first node without acquiring lock
                     && fh == hash
                     && ((fk = f.key) == key || (fk != null && key.equals(fk)))
                     && (fv = f.val) != null)
                return fv;
            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);
                                    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;
                            }
                        }
                        else if (f instanceof ReservationNode)
                            throw new IllegalStateException("Recursive update");
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

이 부분은 ConcurrentHashMap 의 api중 put 부분이다. 

sychronized 부분이 있다. 내용을 읽는 과정에서는 여러 쓰레드가 작동하여 가능하지만, 쓰는 기능에서는 한 배열마다 Lock을 걸어 속도를 높인다. 

개별의 Thread 에서 락을 걸면서 사용하기 때문에 Thread 성능을 높일수 있다.

이러한 이유 때문에 현업에서는 ConcurrentHashMap을 사용하는 것 같다.