Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java에서 컬렉션을 스레드 세이프(Thread-Safe)하게 만드는 방법

멀티스레드 환경에서 여러 스레드가 동시에 하나의 컬렉션에 접근하면 데이터 불일치나 ConcurrentModificationException 같은 문제가 발생할 수 있습니다. Java에서는 java.util 패키지의 Collections 클래스가 제공하는 동기화 메서드를 사용해 컬렉션을 손쉽게 스레드 세이프하게 만들 수 있습니다.

Collections 클래스는 컬렉션 전용으로 동작하는 다양한 유틸리티 메서드를 제공하며, 그중 동기화 관련 메서드들은 다형적 알고리즘(polymorphic algorithms)을 기반으로 각 컬렉션 타입에 맞는 스레드 세이프 래퍼(wrapper)를 반환합니다.

Collections 클래스의 주요 동기화 메서드

번호메서드 및 설명
1static <T> Collection<T> synchronizedCollection(Collection<T> c)
컬렉션 객체를 매개변수로 받아, 지정된 컬렉션을 기반으로 하는 동기화(스레드 세이프) 컬렉션을 반환합니다.
2static <T> List<T> synchronizedList(List<T> list)
List 인터페이스의 객체를 받아, 지정된 리스트를 기반으로 하는 동기화(스레드 세이프) 리스트를 반환합니다.
3static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
Map 인터페이스의 객체를 받아, 지정된 맵을 기반으로 하는 동기화(스레드 세이프) 맵을 반환합니다.
4static <T> Set<T> synchronizedSet(Set<T> s)
Set 인터페이스의 객체를 받아, 지정된 셋을 기반으로 하는 동기화(스레드 세이프) 셋을 반환합니다.
5static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
SortedMap 객체를 받아, 지정된 정렬 맵을 기반으로 하는 동기화(스레드 세이프) 정렬 맵을 반환합니다.
6static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
SortedSet 객체를 받아, 지정된 정렬 셋을 기반으로 하는 동기화(스레드 세이프) 정렬 셋을 반환합니다.

예제 코드

다음 예제에서는 Vector 객체를 생성한 뒤, Collections.synchronizedCollection() 메서드를 사용하여 스레드 세이프한 컬렉션으로 변환하는 과정을 보여줍니다.

import java.util.Collection;
import java.util.Collections;
import java.util.Vector;

public class CollectionSynchronizedExample {
   public static void main(String[] args) {
      // Vector 객체 생성
      Vector<String> vector = new Vector<String>();
      vector.add("JavaFx");
      vector.add("Java");
      vector.add("WebGL");
      vector.add("OpenCV");

      System.out.println(vector);

      // 동기화(스레드 세이프) 컬렉션으로 변환
      Collection<String> synchronizedVector = Collections.synchronizedCollection(vector);
      System.out.println("Synchronized " + synchronizedVector);

      synchronizedVector.add("CoffeeScript");
   }
}

실행 결과

[JavaFx, Java, WebGL, OpenCV]
Synchronized [JavaFx, Java, WebGL, OpenCV]

주의 사항

동기화된 컬렉션을 사용하더라도 반복(iteration) 중에는 별도의 수동 동기화가 필요합니다. 반복문을 사용할 때는 반드시 컬렉션 자체에 대해 synchronized 블록으로 감싸야 안전합니다.

synchronized (synchronizedVector) {
   for (String item : synchronizedVector) {
      System.out.println(item);
   }
}

또한, Java 5 이상에서는 java.util.concurrent 패키지의 CopyOnWriteArrayList, ConcurrentHashMap 등 동시성 컬렉션을 사용하는 것이 성능 면에서 더 유리할 수 있으므로, 상황에 맞게 선택하는 것이 좋습니다.