CountedCompleter<T> — 완료 액션이 있는 ForkJoinTask
CountedCompleter<T> — 완료 액션이 있는 ForkJoinTask
CountedCompleter<T>는 트리거되고 남은 pending 작업이 없을 때 완료 액션을 수행하는 ForkJoinTask 예요. CountedCompleter는 일반적으로 다른 형태의 ForkJoinTask보다 서브태스크 지연·블로킹 상황에서 더 견고하지만, 프로그래밍하기는 덜 직관적이에요.
본문
CountedCompleter의 사용은 CompletionHandler 같은 다른 완료 기반 컴포넌트와 비슷해요. 다만 완료 액션 onCompletion(CountedCompleter)을 트리거하려면 여러 개의 pending 완료가 필요할 수 있어요(단 하나가 아니라).
별도 초기화가 없으면 pending count는 0에서 시작해요. 하지만 setPendingCount(int), addToPendingCount(int), compareAndSetPendingCount(int, int) 메서드로 (원자적으로) 변경할 수 있어요. tryComplete()가 호출되면 pending 액션 수가 0이 아니면 감소시키고, 0이면 완료 액션을 수행하고 이 completer에 completer가 있으면 그 completer로 계속 진행해요. Phaser나 Semaphore 같은 관련 동기화 컴포넌트처럼 이 메서드들은 내부 카운트에만 영향을 주고, 추가적인 내부 장부를 만들지는 않아요. 특히 pending 태스크의 정체성은 유지되지 않아요.
구체적인 CountedCompleter 클래스는 반드시 compute() 메서드를 정의해야 해요. 이 메서드는 대부분의 경우 반환 전에 tryComplete()를 한 번 호출해요. 정상 완료 시 액션을 수행하려면 onCompletion(CountedCompleter)를, 예외 발생 시 액션을 수행하려면 onExceptionalCompletion(Throwable, CountedCompleter)를 선택적으로 오버라이드할 수 있어요.
CountedCompleter는 대부분 결과를 갖지 않아서 보통 CountedCompleter<Void>로 선언하고 항상 null을 결과 값으로 반환해요. 결과가 필요한 경우에는 getRawResult()를 오버라이드해서 join(), invoke() 등에서 결과를 제공하도록 해요. 자신의 completer가 없는 CountedCompleter(getCompleter()가 null)는 일반 ForkJoinTask로 쓸 수 있어요.
CountedCompleter는 트리로 배치할 수 있고, 병렬 재귀 분해(divide-by-two), 검색, 서브태스크 기록, 완료 순회, 트리거 등 다양한 용도로 쓰여요.
생성자
CountedCompleter(CountedCompleter<?> completer, int initialPendingCount) — 주어진 completer와 초기 pending count로 생성해요.
CountedCompleter(CountedCompleter<?> completer) — 주어진 completer와 0의 초기 pending count로 생성해요.
CountedCompleter() — completer가 없고 pending count가 0인 상태로 생성해요.
추상·오버라이드 메서드
abstract void compute() — 이 태스크의 주 계산이에요.
void onCompletion(CountedCompleter<?> caller) — tryComplete()가 호출되고 pending count가 0일 때, 또는 무조건적 complete(T)가 호출될 때 액션을 수행해요. 기본적으로 아무것도 하지 않아요. caller 인자의 정체성을 확인해 this가 아니면 결과를 결합할 서브태스크임을 알 수 있어요.
boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller) — completeExceptionally(Throwable)이 호출되거나 compute()가 예외를 던질 때 액션을 수행해요. true를 반환하고 completer가 아직 완료되지 않았다면 그 completer도 같은 예외로 예외 완료돼요. 기본 구현은 true만 반환해요.
T getRawResult() — 기본적으로 null을 반환해요. Void 액션에는 적합하지만, 다른 경우에는 결과를 담은 필드를 반환하도록 오버라이드해야 해요.
protected void setRawResult(T t) — 기본적으로 아무것도 하지 않아요. 오버라이드는 권장하지 않으며, 하더라도 반드시 스레드 안전해야 해요.
protected final boolean exec() — CountedCompleter의 실행 규약을 구현해요.
pending count 관리 메서드
final CountedCompleter<?> getCompleter() — 생성자에서 설정된 completer를 반환해요.
final int getPendingCount() — 현재 pending count를 반환해요.
final void setPendingCount(int count) — pending count를 주어진 값으로 설정해요.
final void addToPendingCount(int delta) — pending count에 주어진 값을 (원자적으로) 더해요.
final boolean compareAndSetPendingCount(int expected, int count) — 현재 값이 예상 값일 때만 pending count를 (원자적으로) 설정해요.
final int decrementPendingCountUnlessZero() — pending count가 0이 아니면 (원자적으로) 감소시켜요. 원래 값을 반환해요.
완료 메서드
final CountedCompleter<?> getRoot() — 현재 계산의 루트를 반환해요.
final void tryComplete() — pending count가 0이 아니면 감소시키고, 아니면 onCompletion을 호출한 뒤 completer를 완료하려 시도해요.
final void propagateCompletion() — tryComplete()와 동일하지만 완료 경로에서 onCompletion을 호출하지 않아요.
void complete(T rawResult) — pending count와 무관하게 onCompletion을 호출하고 태스크를 완료로 표시하며 completer의 tryComplete()를 트리거해요.
final CountedCompleter<?> firstComplete() — pending count가 0이면 이 태스크를 반환하고, 아니면 감소시키고 null을 반환해요. 완료 순회에 쓰여요.
final CountedCompleter<?> nextComplete() — completer가 없으면 quietlyComplete()를 호출하고 null을 반환해요. completer의 pending count가 0이 아니면 감소시키고 null을, 아니면 completer를 반환해요.
final void quietlyCompleteRoot() — getRoot().quietlyComplete()와 동일해요.
final void helpComplete(int maxTasks) — 완료 경로에 있는 최대 지정 개수의 미처리 태스크를 처리하려 시도해요.
더 알아보기 (Learn more)
- ForkJoinTask 클래스 — 이 태스크의 기반 클래스
- RecursiveAction 클래스 — 결과 없는 병렬 분해 태스크
- Java 공식 API