본문 바로가기
Language/JAVA

[자바 멀티스레딩] 자바 동시성 고급: CountDownLatch 사용법

by YoonJong 2025. 3. 31.
728x90

CountDownLatch 는 스레드 간 동기화를 위해 특정 조건이 완료될 때까지 대기하는 도구이다.

 

목적

- 여러 스레드가 작업을 끝낸 후 다음 단계를 진행하도록 보장

 

동작원리

- 초기 카운트를 설정하고, 각 작업 완료 시 countDown() 으로 카운트 감소

- await() 호출 스레드는 카운트가 0이 될 때까지 블록

 

주요 메서드

- CountDownLatch(int count): 초기 카운트 설정. 작업 수에 맞게 지정

- await(): 카운트가 0이 될 때까지 대기

- await(long timeout, TimeUnit unit): 타임아웃 설정 후 대기

- countDown(): 카운트 1 감소. 0이 되면 대기 해제

- getCount(): 현재 카운트 조회

 

 

장점

- 단순성 : 락보다 직관적이며, 작업 완료 시점에 동기화에 최적

- 유연성 : 작업 수를 카운트로 조정 가능

- 비동기 처리 : 대기 중 다른 스레드 작업 진행 가능

- 타임아웃 지원 : await(long, TimeUnit) 으로 제한 시간 설정 가능

 

단점

- 일회성 : 한번 사용 후 재사용이 불가능. 새 객체 생성 필요

- 원자성 없음 : total += value 같은 연산은 별도 동기화 필요

- 복잡한 조건에 부적합 : 다중 조건은 Condition 이나 다른 도구 권장

- 카운트 관리 : 초기값 잘못 설정 시 데드락 위험 (가장 아래 예제 참고)

 

주의점

- countDown() 누락 : 영원히 대기할 수 있음

- 공유 자원 보호 : total 처럼 공유 변수 시, synchronized 또는 Lock 추가 필요

- 타임아웃 처리: await() 타임아웃 후 로직 필요시 예외 처리

- 스레드 인터럽트 : InterruptedException 에 대한 대비 필요

 

public class LatchExample {

    private int total = 0;
    private final CountDownLatch latch = new CountDownLatch(2);

    public void add(int value) {
        total += value;
        latch.countDown(); // 모든 스레드가 호출 안 하면 await() 영원히 대기
        System.out.println(Thread.currentThread().getName() + " 작업 완료");
    }

    public int getTotal() {
        return total;
    }

    public static void main(String[] args) throws InterruptedException {
        LatchExample example = new LatchExample();

        Thread t1 = new Thread(() -> {
            example.add(10);
        });

        Thread t2 = new Thread(() -> {
            example.add(20);
        });

        t1.start();
        t2.start();

        example.latch.await(); // 두 스레드 완료 대기
        System.out.println("최종 합계: " + example.getTotal());
    }

}


//출력값 (스레드 순서 다를 수 있음)
Thread-0 작업 완료
Thread-1 작업 완료
최종 합계: 30

 

 

CountDownLatch (=초기카운트) 개수와 countDown() 호출 수가 다르면 프로그램이 종료되지 않고 정지 상태발생(데드락)

메인 스레드가 대기 상태로 멈춘다.

예시) 초기카운트가 10개지만 스레드는 2개만 countDown() 호출할경우

private final CountDownLatch latch = new CountDownLatch(10);



Thread t1 = new Thread(() -> {
    example.add(10);
});

Thread t2 = new Thread(() -> {
    example.add(20);
});

 

여기서 마지막에 타임아웃을 설정해놓으면 종료는 가능하다.

example.latch.await(3, TimeUnit.SECONDS);
728x90

댓글