[개발 일기] 2025.04.26 - Collections.synchronizedXXX() vs Concurrent Collections

2025. 4. 26. 13:36·개발 일기

💡 개요

 

오늘은 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
'개발 일기' 카테고리의 다른 글
  • [개발 일기] 2025.04.28 - HTTP 상태코드
  • [개발 일기] 2025.04.27 - Kafka + H2 포트 충돌
  • [개발 일기] 2025.04.25 - 맥북 쿠버네티스 서비스 접근
  • [개발 일기] 2025.04.24 - MongoDB의 쓰기 속도가 빠른 이유?
오도형석
오도형석
  • 오도형석
    형석이의 성장일기
    오도형석
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • MSA 모니터링 서비스
        • DB
      • 스파르타 코딩클럽
        • SQL
        • Spring
      • 백엔드
        • Internet
        • Java
        • DB
      • 캡스톤
        • Django
        • 자연어처리
      • Spring
        • JPA
        • MSA
      • ETC
        • ERROR
      • 개발 일기 N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 인기 글

  • 태그

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
오도형석
[개발 일기] 2025.04.26 - Collections.synchronizedXXX() vs Concurrent Collections
상단으로

티스토리툴바