실시간 알림 기능을 사용할때 Map에 SSE Emitter를 담아야 할 상황이 있었습니다.
이때 ConcurrentHashMap이 사용되어 HashMap, HashTable과 어떤점이 다른지 알아보았습니다.
결론부터 알아보겠습니다.
HashMap | HashTable | ConcurrentHashMap | |
Key/Value null 허용 | O | X | X |
Thread Safe | X | O | O |
스레드 환경 | 싱글 스레드 | 멀티 스레드 | 멀티 스레드 |
속도 | 상 | 하 | 중 |
이제 그 이유를 자세히 알아보겠습니다.
HashMap
HashMap은 Thread Safe하지 않아 싱글스레드에 사용하는것이 좋습니다.
특징
- key와 value에 null을 허용
- 동기화를 보장하지 않음
- 신뢰성이 낮음
- Syncronized를 사용하지 않기때문에 속도가 빠름
put메서드는 putVal입니다.
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)
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;
}
HashTable
HashMap은 Thread Safe하여 멀티스레드에 권장됩니다.
put, get, remove등 메소드에 Syncronized를 사용하여 동기화를 보장하지만
그만큼 멀티스레드 환경에서 속도가 저하됩니다.
특징
- key와 value에 null을 허용하지 않음
- 동기화를 보장
- 신뢰성이 높음
- Syncronized를 사용하하여 속도가 느림
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
ConcurrentHashMap
ConcurrentHashmap 역시 Thread Safe하여 멀티스레드에 권장됩니다.
put, get, remove등 메소드안의 특정 Entry를 조작할때 Syncronized블럭을 사용하여 동기화를 보장합니다.
특정 엔트리에서만 Syncronized를 사용하기 때문에 HashTable보다 멀티스레드 환경에서 속도가 빠릅니다.
특징
- key와 value에 null을 허용하지 않음
- 동기화를 보장
- 신뢰성이 높음
- 특정 Entry에서 Syncronized블럭을 사용하하여 속도가 HashTable보다 빠름
마무리
싱글 스레드를 사용하는 경우는 거의 없다고 생각됩니다.
그러므로 멀티 스레드를 사용하는 환경에서는 메서드 전체가아닌 특정 Entry를 조작할때만 Syncronized블럭을 사용하여
동기화 보장하는 ConcurrentHashMap을 통해 성능을 향상시켜야 하는것을 배웠습니다.
ConcurrentHashMap을 사용한 프로젝트
실시간 알림기능 SSE로 구현하기
앞서 실시간 알림기능을 SSE로 구현하기로 하였습니다.이번글에서는 실시간 알림 기능을 구현해 보겠습니다.서버단의 로직은 생각보다 복잡하여 Front먼저 살펴보겠습니다.클라이언트에서 서버
velog.io
'Java' 카테고리의 다른 글
제네릭 사용 이유 (0) | 2023.09.26 |
---|---|
java.lang 패키지 (0) | 2023.09.26 |
Java - 타입추론 Var (0) | 2023.08.17 |
Optional 클래스 (0) | 2023.08.17 |
StringBuffer, StringBuilder (0) | 2023.08.03 |
실시간 알림 기능을 사용할때 Map에 SSE Emitter를 담아야 할 상황이 있었습니다.
이때 ConcurrentHashMap이 사용되어 HashMap, HashTable과 어떤점이 다른지 알아보았습니다.
결론부터 알아보겠습니다.
HashMap | HashTable | ConcurrentHashMap | |
Key/Value null 허용 | O | X | X |
Thread Safe | X | O | O |
스레드 환경 | 싱글 스레드 | 멀티 스레드 | 멀티 스레드 |
속도 | 상 | 하 | 중 |
이제 그 이유를 자세히 알아보겠습니다.
HashMap
HashMap은 Thread Safe하지 않아 싱글스레드에 사용하는것이 좋습니다.
특징
- key와 value에 null을 허용
- 동기화를 보장하지 않음
- 신뢰성이 낮음
- Syncronized를 사용하지 않기때문에 속도가 빠름
put메서드는 putVal입니다.
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)
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;
}
HashTable
HashMap은 Thread Safe하여 멀티스레드에 권장됩니다.
put, get, remove등 메소드에 Syncronized를 사용하여 동기화를 보장하지만
그만큼 멀티스레드 환경에서 속도가 저하됩니다.
특징
- key와 value에 null을 허용하지 않음
- 동기화를 보장
- 신뢰성이 높음
- Syncronized를 사용하하여 속도가 느림
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
ConcurrentHashMap
ConcurrentHashmap 역시 Thread Safe하여 멀티스레드에 권장됩니다.
put, get, remove등 메소드안의 특정 Entry를 조작할때 Syncronized블럭을 사용하여 동기화를 보장합니다.
특정 엔트리에서만 Syncronized를 사용하기 때문에 HashTable보다 멀티스레드 환경에서 속도가 빠릅니다.
특징
- key와 value에 null을 허용하지 않음
- 동기화를 보장
- 신뢰성이 높음
- 특정 Entry에서 Syncronized블럭을 사용하하여 속도가 HashTable보다 빠름
마무리
싱글 스레드를 사용하는 경우는 거의 없다고 생각됩니다.
그러므로 멀티 스레드를 사용하는 환경에서는 메서드 전체가아닌 특정 Entry를 조작할때만 Syncronized블럭을 사용하여
동기화 보장하는 ConcurrentHashMap을 통해 성능을 향상시켜야 하는것을 배웠습니다.
ConcurrentHashMap을 사용한 프로젝트
실시간 알림기능 SSE로 구현하기
앞서 실시간 알림기능을 SSE로 구현하기로 하였습니다.이번글에서는 실시간 알림 기능을 구현해 보겠습니다.서버단의 로직은 생각보다 복잡하여 Front먼저 살펴보겠습니다.클라이언트에서 서버
velog.io
'Java' 카테고리의 다른 글
제네릭 사용 이유 (0) | 2023.09.26 |
---|---|
java.lang 패키지 (0) | 2023.09.26 |
Java - 타입추론 Var (0) | 2023.08.17 |
Optional 클래스 (0) | 2023.08.17 |
StringBuffer, StringBuilder (0) | 2023.08.03 |