본문 바로가기
Java/Java이론

Collections.synchronizedList() :멀티스레드 환경에서 안전한 리스트 사용하기

by P_eli 2024. 12. 13.
728x90
반응형

멀티스레드 환경에서 리스트(List) 와 같은 데이터 구조를 안전하게 다루는 것은 매우 중요합니다. 자칫 잘못하면 데이터 무결성이 깨지거나, 예상치 못한 예외가 발생할 수 있기 때문입니다. 이를 해결하기 위한 방법 중 하나가 바로 Collections.synchronizedList() 를 사용하는 것입니다.

 

Collections.synchronizedList()란?

Collections.synchronizedList()는 Java에서 제공하는 동기화된 리스트를 생성하는 메서드입니다. 일반적으로 ArrayList와 같은 리스트는 스레드에 안전하지 않기 때문에, 여러 스레드가 동시에 접근하면 데이터 충돌이 발생할 수 있습니다. 하지만 synchronizedList()를 사용하면 동기화 처리가 자동으로 적용되어 멀티스레드 환경에서도 안전하게 리스트를 사용할 수 있습니다.

 

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SynchronizedListExample {
    public static void main(String[] args) {
        // 동기화된 리스트 생성
        List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

        // 동기화 블록 내에서 리스트 작업 수행
        synchronized (synchronizedList) {
            synchronizedList.add("Thread-Safe");
            synchronizedList.add("Example");
            System.out.println(synchronizedList);
        }
    }
}

 

 

실행결과

 

 

동기화 블록(synchronized block)의 필요성

Collections.synchronizedList()를 사용하면 리스트의 기본적인 연산(add, remove 등)은 동기화 처리됩니다. 하지만 **반복(iteration)**과 같은 복잡한 작업은 여전히 동기화 블록으로 감싸야 안전합니다. 이는 반복 중에 다른 스레드가 리스트를 수정하면 ConcurrentModificationException이 발생할 수 있기 때문입니다.

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

 

활용 예제: 멀티스레드 환경에서의 사용

아래는 여러 스레드가 동기화된 리스트에 동시에 접근하는 예제입니다.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MultiThreadedSynchronizedList {
    public static void main(String[] args) {
        List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());

        // 스레드 생성 및 실행
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                synchronizedList.add(i);
                System.out.println("Thread 1 added: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 5; i < 10; i++) {
                synchronizedList.add(i);
                System.out.println("Thread 2 added: " + i);
            }
        });

        thread1.start();
        thread2.start();

        // 스레드가 완료될 때까지 대기
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 동기화 블록으로 리스트 출력
        synchronized (synchronizedList) {
            System.out.println("Final List: " + synchronizedList);
        }
    }
}

 

실행결과

 

장단점

장점

  1. 간단한 동기화 처리: synchronizedList()를 사용하면 최소한의 코드로 동기화를 처리할 수 있습니다.
  2. Java 내장 지원: 추가 라이브러리 없이 Java 표준 라이브러리로 구현 가능합니다.

단점

  1. 복잡한 작업의 추가 동기화 필요: 반복과 같은 작업은 여전히 synchronized 블록이 필요합니다.
  2. 성능 저하: 모든 접근이 동기화되므로, 병렬 처리 성능이 낮아질 수 있습니다.

언제 사용할까?

Collections.synchronizedList()는 다음과 같은 경우에 적합합니다:

  • 동시성 문제가 발생할 가능성이 있는 간단한 리스트 작업.
  • Java 5 이전 버전에서 동기화가 필요한 경우. (Java 5 이상에서는 CopyOnWriteArrayList와 같은 대체 옵션도 고려 가능.)

결론

멀티스레드 환경에서 리스트를 안전하게 사용하는 것은 매우 중요합니다. Collections.synchronizedList()는 이를 위한 쉽고 간단한 해결책을 제공합니다. 그러나 성능이나 복잡한 동기화 요구사항이 있다면, CopyOnWriteArrayListConcurrentLinkedQueue와 같은 더 발전된 동시성 컬렉션도 고려해볼 가치가 있습니다.

728x90
반응형