💡 개요
오늘은 Collection 자료형의 동기화 환경을 제공하는 기술에 대해 정리해보자.
🛠️ Collections.synchronizedXXX()
Java에서는 Collections 유틸리티 클래스에서 제공하는 synchronizedXXX() 메서드를 통해 동기화된 컬렉션을 생성할 수 있다.
예를 들어 Collections.synchronizedList()는 List에 대한 synchronized wrapper를 반환한다.
예시 코드는 다음과 같다.
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add("apple");
syncList.add("banana");
// 읽기 작업 시에도 외부에서 동기화 필요 (Iterator 사용 시)
synchronized (syncList) {
for (String fruit : syncList) {
System.out.println(fruit);
}
}
참고로 synchronizedXXX() 이 지원되는 자료형은 여러가지가 있다.
synchronizedXXX() 방식으로 동기화된 컬렉션 객체를 생성하면 모든 메서드에 대해 확실한 동기화환경을 제공할 수 있다는 장점이 있다.
하지만 그 만큼 동기화 환경을 제공하기 위해 성능 저하가 발생할 수 있기 때문에 다음과 같은 상황에서만 사용하는 것이 좋다.
- 간단하게 구현하고자 할 때!
- 개발자가 따로 코드를 구현할 필요 없이 컬렉션 객체를 생성할 때만 선언하면 됨
- 고성능이 중요하지 않은 경우!
- synchronizedXXX()으로 생성된 자료형에 접근하는 영역은 모두 임계 영역이 된다.
- 그렇기 때문에 해당 영역은 하나의 스레드만 접근이 가능해지기 때문에 병목 현상이 발생할 수 있다.
🛠️ Concurrent Collections
Concurrent Collections 방식은 synchronizedXXX() 보다 더 정교한 동기화 환경을 제공하고, 고성능이 중요한 상황에 사용된다.
⚙️ ConcurrentHashMap
기본적으로 제공되는 HashMap은 멀티스레드 환경에서 안전하지 않다.
하지만 ConcurrentHashMap 의 경우엔 내부적으로 필요한 부분에만 한정적으로 락을 걸기 때문에 성능 측면에서 유리하다.
Map<String, Integer> map = new ConcurrentHashMap<>();
// A 스레드
map.put("apple", 3);
// B 스레드
map.put("banana", 2);
// C 스레드
System.out.println(map.get("apple"));
⚙️ CopyOnWriteArrayList
CopyOnWriteArrayList 방식은 리스트에 get() 작업이 자주 발생하고, add(), remove() 즉 리스트의 데이터에 변경을 가하는 작업이 적은 경우에 적합하다.
왜냐하면 add()나 remove() 작업이 발생할 때마다 기존 배열을 전체 복사하여 새로운 배열을 만들고, 그 배열에 변경사항을 반영하기 때문에 이 과정에서 지연시간이나 많은 비용이 소모될 수 있다.
하지만 get() 로직을 수행하는 도중에도 락 없이 안전하게 사용 가능하기 때문에 읽기 작업에 있어서는 synchronizedXXX() 보다 훨씬 성능 측면에서 유리하다.
List<String> list = new CopyOnWriteArrayList<>();
list.add("apple");
list.add("banana");
// 여러 스레드에서 동시에 읽기 가능
for (String item : list) {
System.out.println(item);
}
// 쓰기 작업도 가능하지만, 매번 내부 배열이 복사되므로 빈번한 쓰기에는 부적합
list.add("orange");
🤔 결론
만약 동기화 환경이 구축되어 있는 상황에서는 일반 컬렉션 객체를 사용해도 될 것 같다.
하지만 클래스의 필드와 같은 공유되는 컬렉션일 경우엔 쓰기나 읽기 작업 모두 빈번하게 발생하는 경우엔 synchronizedXXX() 을 고려하면 좋을 것 같고, 적은 양의 데이터를 다루는 작업에선 Concurrent Collections 을 고려하는 것이 좋을 듯 하다.
'개발 일기' 카테고리의 다른 글
[개발 일기] 2025.04.28 - HTTP 상태코드 (0) | 2025.04.28 |
---|---|
[개발 일기] 2025.04.27 - Kafka + H2 포트 충돌 (0) | 2025.04.27 |
[개발 일기] 2025.04.25 - 맥북 쿠버네티스 서비스 접근 (0) | 2025.04.25 |
[개발 일기] 2025.04.24 - MongoDB의 쓰기 속도가 빠른 이유? (0) | 2025.04.24 |
[개발 일기] 2025.04.23 - 왜 두 번씩? (0) | 2025.04.23 |