개발/자바

Collections : synchronized (list) {} 동기화 블록

피터JK 2025. 2. 20. 11:04
728x90

1. 동기화 블록의 필요성

자바의 Collections.synchronizedList() 메서드를 사용하면 동기화된 리스트를 만들 수 있습니다.
하지만, 반복문 (for-each, Iterator)을 사용할 때는 추가적인 동기화 블록이 필요합니다.
이유는 반복문 실행 중 리스트가 수정될 경우 ConcurrentModificationException이 발생할 가능성이 있기 때문입니다.


2. 코드 분석

synchronized (list) { // 🔹 리스트 전체에 대한 동기화 블록
    for (String s : list) { // 🔹 리스트 요소를 순회
        System.out.println(s);
    }
}

✔ synchronized (list) {...}

  • 리스트 전체에 대한 락(lock)을 걸어 여러 스레드에서 동시에 접근하는 것을 방지
  • 이 블록 내부에서는 다른 스레드가 리스트를 수정할 수 없음

✔ for (String s : list) { ... }

  • 동기화된 상태에서 리스트의 요소를 하나씩 가져와 출력
  • 리스트가 수정되는 동안 반복문이 실행되면 예외(ConcurrentModificationException) 발생 가능하므로 동기화 필요

3. 예제 코드: 동기화 없이 발생하는 문제

synchronized 블록이 없을 때 여러 스레드가 리스트에 동시에 접근하면 예외 발생 가능

import java.util.*;

public class SynchronizedListExample {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        list.add("A");
        list.add("B");
        list.add("C");

        // 새 스레드에서 리스트 수정
        new Thread(() -> {
            list.add("D");
            list.remove("A");
        }).start();

        // 메인 스레드에서 리스트 순회 (동기화 안 하면 위험!)
        for (String s : list) {
            System.out.println(s); // 🔴 동기화하지 않으면 ConcurrentModificationException 발생 가능
        }
    }
}

출력 예시 (예외 발생 가능)

A
B
Exception in thread "main" java.util.ConcurrentModificationException

list를 반복문으로 읽는 도중, 다른 스레드가 수정하면 예외 발생


4. 해결 방법: 동기화 블록 추가

synchronized 블록을 사용하여 반복문 실행 중 다른 스레드가 접근하지 못하도록 보호

import java.util.*;

public class SynchronizedListExample {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        list.add("A");
        list.add("B");
        list.add("C");

        new Thread(() -> {
            synchronized (list) { // 🔹 리스트 수정 시 동기화
                list.add("D");
                list.remove("A");
            }
        }).start();

        synchronized (list) { // 🔹 반복문 실행 시 동기화
            for (String s : list) {
                System.out.println(s);
            }
        }
    }
}

출력 예시 (정상 실행, 예외 없음)

B
C
D

🔹 synchronized (list) {...} 블록을 사용하여 안전하게 동기화된 상태에서 리스트를 순회


5. 결론

  1. 동기화 리스트 (Collections.synchronizedList())를 사용해도 반복문 실행 중에는 추가적인 synchronized 블록이 필요
  2. 리스트를 읽는 동안 다른 스레드가 수정하면 ConcurrentModificationException 발생 가능
  3. 반복문 실행 시 synchronized (list) {...}로 동기화하면 안전하게 접근 가능
  4. 멀티스레드 환경에서 공유 리스트를 사용할 때는 항상 동기화 고려 필요

synchronized (list) {} 블록은 멀티스레드 환경에서 컬렉션을 안전하게 사용할 때 필수! 

728x90