Phaser — 유연한 재사용 배리어

Phaser — 유연한 재사용 배리어

Phaser재사용 가능한 동기화 배리어로, 기능적으로 CyclicBarrierCountDownLatch와 비슷하지만 더 유연한 사용을 지원해요. 등록 파티 수가 시간에 따라 달라질 수 있다는 점이 핵심 차이예요.

출처: Java API Reference

본문

개념 이해하기

PhaserCyclicBarrierCountDownLatch와 비슷하지만 더 유연한, 재사용 가능한 동기화 배리어예요.

public class Phaser
extends Object

등록 (Registration)

다른 배리어와 달리 phaser에 동기화하기 위해 등록되는 파티 수는 시간에 따라 달라질 수 있어요. 태스크는 언제든 등록(register(), bulkRegister(int), 또는 초기 파티 수를 설정하는 생성자)할 수 있고, 도착 시 선택적으로 등록 해제(arriveAndDeregister())할 수 있어요. 등록·해제는 내부 카운트에만 영향을 미치며, 태스크는 자신이 등록됐는지 조회할 수 없어요 (서브클래스화로 추가 가능).

동기화

CyclicBarrier처럼 Phaser는 반복해서 대기할 수 있어요. arriveAndAwaitAdvance()CyclicBarrier.await과 유사한 효과예요. phaser의 각 세대(generation)는 연관된 위상(phase) 번호를 가지며, 0에서 시작해 모든 파티가 도착하면 진행(advance)되고 Integer.MAX_VALUE에 도달하면 0으로 돌아가요.

  • Arrival(도착): arrive()arriveAndDeregister()는 도착을 기록해요. 이 메서드는 블로킹하지 않고 연관된 도착 위상 번호를 반환해요. 주어진 위상의 마지막 파티가 도착하면 선택적 액션이 수행되고 위상이 진행돼요. 이 액션은 위상 진행을 촉발한 파티가 수행하며, 종료를 제어하는 onAdvance(int,int)를 오버라이드해 배치돼요.
  • Waiting(대기): awaitAdvance(int)는 도착 위상 번호를 나타내는 인자를 요구하고, phaser가 다른 위상으로 진행될 때(또는 이미 다른 위상일 때) 반환해요. 대기 중인 스레드가 인터럽트돼도 계속 기다려요. 인터럽트 가능·타임아웃 변형도 있지만, 그 예외가 phaser의 상태를 바꾸진 않아요. Phaser는 ForkJoinPool에서 실행되는 태스크에도 쓰일 수 있어요.

종료 (Termination)

isTerminated()로 확인할 수 있는 종료 상태에 들어갈 수 있어요. 종료 시 모든 동기화 메서드는 진행을 기다리지 않고 즉시 반환하며(음수 반환값으로 표시), 종료 후 등록 시도는 효과가 없어요. onAdvance 호출이 true를 반환하면 종료가 촉발돼요. 기본 구현은 등록 해제로 등록 파티 수가 0이 되면 true를 반환해요. forceTermination()은 대기 스레드를 급작스레 해제하고 종료시키는 데도 쓸 수 있어요.

계층화 (Tiering)

Phaser는 경합을 줄이기 위해 계층 구조(트리)로 구성될 수 있어요. 파티 수가 많아 동기화 경합 비용이 큰 경우, 하위 phaser 그룹이 공통 부모를 공유하게 만들 수 있어요. 트리에서 자식 phaser의 부모 등록·해제는 자동으로 관리돼요.

모니터링

동기화 메서드는 등록 파티만 호출할 수 있지만, phaser의 현재 상태는 모든 호출자가 모니터링할 수 있어요. getRegisteredParties(), getArrivedParties(), getPhase(), getUnarrivedParties()가 있어요.

메모리 일관성 효과: 어떤 형태의 arrive 메서드 이전의 동작은 대응하는 위상 진행과 onAdvance 액션(있다면)보다 happen-before이고, 이는 다시 위상 진행에 뒤따르는 동작보다 happen-before예요.

활용 예 — CountDownLatch 대체 (일회성)

void runTasks(List<Runnable> tasks) {
    Phaser startingGate = new Phaser(1);       // "1" to register self
    for (Runnable task : tasks) {
        startingGate.register();
        new Thread(() -> {
            startingGate.arriveAndAwaitAdvance();
            task.run();
        }).start();
    }
    startingGate.arriveAndDeregister();        // deregister self to allow threads to proceed
}

생성자

public Phaser() — 등록 파티 없음, 부모 없음, 초기 위상 0. 사용하려면 먼저 등록해야 해요.

public Phaser(int parties) — 주어진 등록·미도착 파티 수, 부모 없음, 초기 위상 0.

  • IllegalArgumentException

public Phaser(Phaser parent)Phaser(parent, 0)와 동등.

public Phaser(Phaser parent, int parties) — 주어진 부모와 파티 수. 부모가 non-null이고 파티 수가 0보다 크면 자식 phaser가 부모에 등록돼요.

  • IllegalArgumentException

등록·도착 메서드

public int register() — 새 미도착 파티를 추가해요. 종료된 phaser면 등록이 효과 없고 음수 반환.

  • IllegalStateException (최대 파티 초과)

public int bulkRegister(int parties) — 주어진 수의 미도착 파티를 추가해요.

  • IllegalStateException, IllegalArgumentException (parties < 0)

public int arrive() — 다른 사람이 도착하길 기다리지 않고 이 phaser에 도착해요. 도착 위상 번호 또는 종료 시 음수 반환.

public int arriveAndDeregister() — 다른 사람을 기다리지 않고 도착하며 등록을 해제해요. 해제는 향후 위상 진행에 필요한 파티 수를 줄여요. 부모가 있고 0 파티가 되면 부모에서도 해제돼요.

public int arriveAndAwaitAdvance() — 도착하고 다른 사람을 기다려요. awaitAdvance(arrive())와 동등.

대기 메서드

public int awaitAdvance(int phase) — 주어진 위상 값에서 phaser의 위상이 진행될 때까지 기다려요. 현재 위상이 주어진 값과 다르거나 종료됐으면 즉시 반환.

  • phase — 보통 이전 arrive/arriveAndDeregister 반환값

public int awaitAdvanceInterruptibly(int phase) throws InterruptedException — 인터럽트되면 InterruptedException을 던지는 대기 변형.

public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException — 타임아웃 대기 변형.

종료·상태 메서드

public void forceTermination() — 이 phaser를 종료 상태로 강제해요. 계층화 세트면 세트의 모든 phaser가 종료돼요. 예외 후 복구 조정에 유용.

public final int getPhase() — 현재 위상 번호. Integer.MAX_VALUE 후 0으로 재시작. 종료 시 음수.

public int getRegisteredParties() — 등록된 파티 수.

public int getArrivedParties() — 현재 위상에 도착한 등록 파티 수.

public int getUnarrivedParties() — 아직 도착하지 않은 등록 파티 수.

public Phaser getParent() — 부모 또는 null.

public Phaser getRoot() — 루트 조상 (부모 없으면 자기 자신).

public boolean isTerminated() — 종료됐으면 true.

**protected boolean onAdvance(int phase, int registeredParties)``** — 임박한 위상 진행 시 액션 수행·종료 제어를 위한 오버라이드 메서드. true를 반환하면 종료 상태로 설정돼요. 계층화 세트에선 루트 phaser에서만 호출돼요. 기본 구현은 arriveAndDeregister로 등록 파티가 0이 되면 true. 연속 등록 시 계속 진행하게 하려면 항상 false`를 반환하도록 오버라이드하세요.

public String toString()"phase = ...", "parties = ...", "arrived = ..."를 담은 상태 문자열.

더 알아보기 (Learn more)