
자바 멀티스레드 프로그래밍에서 여러 스레드의 작업 완료 시점을 맞추거나 특정 지점에서 스레드들을 대기시키는 것은 매우 까다로운 작업입니다. 자바의 java.util.concurrent 패키지는 이를 위해 CountDownLatch와 CyclicBarrier라는 두 가지 강력한 동기화 도구를 제공합니다. 비슷해 보이지만 그 목적과 작동 방식은 판이하게 다릅니다. 본 글에서는 실무에서 이들을 어떻게 선택하고 활용해야 하는지 전문적으로 분석해 보겠습니다.
1. CountDownLatch: "결승선에서 기다리는 심판"
CountDownLatch는 하나 이상의 스레드가 다른 스레드들의 일련의 작업이 완료될 때까지 기다리도록 하는 동기화 도구입니다. 카운트다운(Count Down)이라는 이름처럼 설정된 숫자가 0이 될 때까지 대기 중인 스레드를 붙잡아 둡니다.
- 동작 원리: 생성자에서 설정한 숫자를
countDown()메서드가 호출될 때마다 1씩 감소시키며, 0이 되는 순간await()를 호출하여 대기하던 스레드들이 해제됩니다. - 특징: 일회용입니다. 한 번 0이 되면 다시 사용할 수 없습니다.
2. CyclicBarrier: "모두 모여야 출발하는 투어 가이드"
CyclicBarrier는 여러 스레드가 서로를 특정 지점(Barrier)에서 기다리게 하여, 모든 스레드가 그 지점에 도착했을 때 다음 단계를 진행하도록 합니다.
- 동작 원리: 모든 스레드가
await()를 호출하여 정해진 숫자의 스레드가 모이면 장벽이 무너지고 다음 로직이 실행됩니다. - 특징: 이름에 'Cyclic'이 들어간 것처럼, 장벽이 무너진 후
reset()을 통해 재사용이 가능합니다.
3. CountDownLatch vs CyclicBarrier 핵심 비교
두 도구의 결정적인 차이점을 한눈에 파악할 수 있도록 표로 정리했습니다.
| 비교 항목 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 주요 목적 | 다른 스레드의 작업 완료를 기다림 | 스레드 간의 상호 대기 및 동기화 |
| 재사용성 | 재사용 불가 (일회용) | 재사용 가능 (Cyclic) |
| 대기 스레드 | 주로 메인 스레드나 관리 스레드가 대기 | 작업을 수행하는 모든 스레드가 대기 |
| 핵심 메서드 | countDown(), await() | await() |
| Barrier Action | 지원하지 않음 | 모든 스레드 도착 시 별도 로직 실행 가능 |
4. 실무 코드 샘플: 언제 무엇을 써야 할까?
사례 A: 서비스 시작 전 필수 리소스 초기화 (CountDownLatch)
여러 개의 초기화 모듈이 완료되어야 메인 서버가 구동되는 시나리오에 적합합니다.
CountDownLatch latch = new CountDownLatch(3);
// 3개의 모듈이 각각 완료 시 latch.countDown() 호출
// 메인 스레드
latch.await(); // 3개가 모두 끝나야 통과
System.out.println("모든 리소스 초기화 완료. 서버 구동!");
사례 B: 병렬 데이터 처리 후 단계별 합산 (CyclicBarrier)
각 스레드가 각자의 파트를 계산하고, 모두 모여 합계 결과를 낸 뒤 다음 단계로 넘어가는 반복 작업에 적합합니다.
CyclicBarrier barrier = new CyclicBarrier(4, () -> {
System.out.println("모든 스레드 도착! 합산 로직 실행");
});
// 4개의 스레드가 각각 연산 수행 후
barrier.await(); // 4명이 다 모여야 다음 코드로 진행
5. 전문 개발자를 위한 최적화 팁
- 타임아웃 활용:
await(long timeout, TimeUnit unit)을 사용하여 특정 스레드의 문제로 인해 시스템 전체가 영원히 대기 상태(Deadlock)에 빠지는 것을 방지하십시오. - 예외 처리: CyclicBarrier는 한 스레드라도 중단되면
BrokenBarrierException을 발생시킵니다. 트랜잭션의 원자성을 위해 적절한 복구 로직이 필요합니다. - 메모리 가시성: 두 도구 모두 내부적으로
volatile필드와 CAS 연산을 활용하여 스레드 간의 메모리 가시성을 보장합니다.
6. 결론: 선택의 기준
CountDownLatch는 '어떤 사건'들이 일어나는지를 체크할 때 유용하며, CyclicBarrier는 '참여자'들이 서로를 기다려야 할 때 유용합니다. 재사용이 필요한가? 참여 스레드가 모두 대기해야 하는가? 이 두 가지 질문만으로도 여러분은 상황에 맞는 최적의 동기화 도구를 선택할 수 있습니다.
내용 출처 및 참고 문헌:
- Java Platform, Standard Edition 17 API Specification
- Java Concurrency in Practice (Brian Goetz)
- Baeldung: CountDownLatch vs CyclicBarrier in Java
'Language > Java' 카테고리의 다른 글
| [JAVA] Java 8의 혁신 : 현대적 프로그래밍의 기점이 된 주요 변화들 (0) | 2026.01.22 |
|---|---|
| [JAVA] 메모리 누수(Memory Leak) 사례와 해결 방안 (0) | 2026.01.22 |
| [JAVA] Atomic 변수와 CAS 알고리즘 : 멀티스레드 환경의 성능 혁신 (0) | 2026.01.22 |
| [JAVA] JVM 메모리 구조의 심층 분석 : 효율적 자원 관리의 핵심 Runtime Data Areas (0) | 2026.01.22 |
| [JAVA] Garbage Collector(GC) 완벽 가이드 : Serial부터 ZGC까지 핵심 정리 (0) | 2026.01.22 |