du.study기록공간

Java Map java.util.concurrent.ConcurrentHashMap 본문

자바

Java Map java.util.concurrent.ConcurrentHashMap

du.study 2020. 4. 6. 21:38
728x90

이번에는 Map의 구현체인 ConcurrentHashMap에 대하여 작성하려합니다.

이전에 작성했던 HashMap을 보면 내부적으로 동기화를 보장해주는 부분은 없습니다.

(즉 멀티스레드 환경에서 HashMap은 동기화를 보장해 주지 않는다.)

 

그렇다면 HashTable은 어떨까?

HashTable의 메서드를  간략하게 살펴보면 다음과 같습니다.

public synchronized boolean contains(Object value)

public synchronized boolean containsKey(Object key) 

public synchronized V get(Object key) 

public synchronized V put(K key, V value) 

public synchronized V remove(Object key)

public synchronized void putAll(Map<? extends K, ? extends V> t)

public synchronized void clear() 

모든 메소드에 synchronized가 걸려 동기화를 보장해줍니다. 하지만 반대로 HashMap에는 다음과 같은 synchronized가 없기에 멀티쓰레드 환경에서 동기화를 보장해주지 못하게 됩니다..

 

 

물론 HashTable을 사용하지않아도 Map을 동기화하는 방법엔 Collections.synchronizedMap 을 이용하는 방법이 있습니다.

Map<String,String> map2 = Collections.synchronizedMap(new HashMap<>());

내부 함수
public int size() {
   synchronized(this.mutex) {
       return this.m.size();
   }
}

public boolean isEmpty() {
    synchronized(this.mutex) {
       return this.m.isEmpty();
    }
}

public boolean containsKey(Object key) {
    synchronized(this.mutex) {
        return this.m.containsKey(key);
    }
}

내부적으로 Map기능에 대해 syncgronized 구문을 매핑하여 전체적인 동기화를 보장해주게 됩니다.

하지만 해당방법은 Map전체를 동기화하게되면서 Multi Thread환경에서 성능을 저하하게 됩니다.

 

요거의 대안? 으로 사용되는 ConcurrentHashMap를 살펴보면 다음과 같습니다.

(메서드 전체를 기록할 순 없기에 대표부분 한가지를 기록하려합니다.)

final V putVal(K key, V value, boolean onlyIfAbsent) {
   ....
   ConcurrentHashMap.Node f;
   int i;
   if ((f = tabAt(tab, i = n - 1 & hash)) == null) {
          ....
   } else {
          ....
           V oldVal = null;
           synchronized(f) {
               if (tabAt(tab, i) == f) {
                   if (fh < 0) {
                       if (f instanceof ConcurrentHashMap.TreeBin) {
                          ....  
                       }
                   } else {
                       label124: {
                           ConcurrentHashMap.Node e;
                           for(e = f; e.hash != hash || (ek = e.key) != key && (ek == null || !key.equals(ek)); ++binCount) {
                               ConcurrentHashMap.Node<K, V> pred = e;
                               if ((e = e.next) == null) {
                                   pred.next = new ConcurrentHashMap.Node(hash, key, value);
                                   break label124;
                               }
                           }

                           oldVal = e.val;
                           if (!onlyIfAbsent) {
                               e.val = value;

내부 내용을 매우 축소했지만, 살펴보면 먼저 버킷에 있는 ConcurrentHashMap.Node를 가져옵니다.

만약 해당 버킷이 비어있다면 그냥 추가를 하게되지만, 기존에 존재할 경우, synchronized를 걸어주고 추가를 하게 됩니다.

 

반대로 get을 살펴보면  

public V get(Object key) {
    int h = spread(key.hashCode());
    ConcurrentHashMap.Node[] tab;
    ConcurrentHashMap.Node e;
    int n;
    if ((tab = this.table) != null && (n = tab.length) > 0 && (e = tabAt(tab, n - 1 & h)) != null) {
        int eh;
        Object ek;
        if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || ek != null && key.equals(ek)) {
                return e.val;
            }
        } else if (eh < 0) {
            ConcurrentHashMap.Node p;
            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;
}

synchronized가 없이 get이 가능하게 됩니다. 즉. 1번 버킷에서 synchronized를 걸고 작업중이라도, 2번 버킷에 대해 get을 하는경우 조회가 가능한 구조입니다.

 

Map전체적으로 synchronized를 걸지않기에 멀티쓰래드 환경에서 조회성능이 향상되게 됩니다.

단 putAll 같이 내부적으로 putval을 계속 호출하는 메서드의 동작동안은 get의 결과가 원하지 않는 결과가 나올 수 있는 상황이 생길 순 있습니다.

 

해당 구현체가 있는것만 알고 사실 사용해본적은 없는데, 만약 멀티스레드 환경에서 사용할 상황이 생기면 참고하여 적용을 한번 해봐야겠습니다.

 

728x90

'자바' 카테고리의 다른 글

Java Optional 사용하기  (0) 2020.05.17
Java stream sort multiple fields  (0) 2020.04.25
java.util.Set(1) HashSet  (0) 2020.03.28
Java.util.List Interface(3) Stack  (0) 2020.03.27
Java.util.List Interface(2) LinkedList  (0) 2020.03.26
Comments