티스토리 뷰
CountDownLatch는 하나 이상의 스레드가 다른 스레드들의 작업 완료를 기다릴 수 있게 해주는 일회성 동기화 도구(synchronization aid) 이다. "일회성"이라고 표현한 이유는 한 번 사용하면 재사용 할 수 없다는 뜻이다. 동시성 테스트에서는 스레드들의 동시 시작을 조율 하는 용도로 활용된다.
핵심 동작 원리
생성 시 카운트 값을 지정하고, countDown() 호출 시 카운트를 1씩 감소시킨다. await()을 호출한 스레드는 카운트가 0이 될 때까지 블로킹된다. 카운트가 0이 되는 순간 await()에서 대기하던 모든 스레드가 실행을 재개한다.
- 카운트가 0에 도달하면 리셋할 수 없다.
- 카운트가 이미 0인 상태에서
countDown()을 호출하면 아무 일도 일어나지 않는다. - 카운트가 이미 0인 상태에서
await()을 호출하면 블로킹 없이 즉시 통과한다.
주요 메서드
| 메서드 | 설명 |
|---|---|
countDown() |
카운트를 1 감소. 0이 되면 대기 중인 스레드들이 깨어남 |
await() |
카운트가 0이 될 때까지 블로킹 |
await(long timeout, TimeUnit unit) |
타임아웃 지정. 시간 내 0이 되면 true, 초과하면 false 반환. 무한 대기 방지용으로 실무에서 권장 |
getCount() |
현재 남은 카운트 반환. 디버깅/로깅 용도. TOCTOU 문제로 분기 로직에 사용하면 안 됨 |
동시 시작 조율 패턴
동시성 테스트에서 여러 스레드의 동시 시작을 조율하는 패턴이다.
int threadCount = 200;
CountDownLatch startSignal = new CountDownLatch(1); // 동시 시작용
CountDownLatch doneSignal = new CountDownLatch(threadCount); // 완료 대기용
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
for (int i = 0; i < threadCount; i++) {
executor.submit(() -> {
try {
startSignal.await(); // 모든 스레드가 여기서 대기 동시 실행 보장
doWork();
} finally {
doneSignal.countDown(); // 작업 완료 알림
}
});
}
startSignal.countDown(); // 카운트 1→0, 모든 스레드 동시 출발
doneSignal.await(); // 모든 작업 완료 대기
executor.shutdown();
흐름 정리:
executor.submit()은 태스크를 스레드 풀의 작업 큐에 넣고 즉시 반환- 각 워커 스레드가 태스크를 꺼내 실행하면서
startSignal.await()에서 블로킹 - for 루프 완료 후 메인 스레드가
startSignal.countDown()호출 → 카운트 0 → 전체 스레드 동시 출발 - 각 스레드 완료 시
doneSignal.countDown()호출 - 메인 스레드는
doneSignal.await()에서 전체 완료 대기
동시 시작이 완벽하게 보장되지 않을 수 있다.
모든 스레드가startSignal.await()에 도달하기 전에startSignal.countDown()이 호출될 수 있다. 이 경우 뒤늦게await()을 호출한 스레드는 블로킹 없이 즉시 통과하므로, 먼저 도착한 스레드와 시작 시점 차이가 발생한다.
'라이브러리&프레임워크 > Spring' 카테고리의 다른 글
| @Transactional의 readOnly 속성의 의미 (0) | 2026.04.21 |
|---|---|
| SLA, SLO, SLI 개념 정리 (0) | 2026.04.19 |
| Application Event란?? (0) | 2026.03.28 |
| 이커머스 좋아요 집계 방식 비교 (0) | 2026.03.15 |
| 동시성 전략, 경합 빈도만 보면 안 되는 이유 (0) | 2026.03.09 |
