Files
java-examples/docs/Concurrency/06_Runnable,Callable,Future.md

8.8 KiB

자바의 Runnable, Callable, Future, CompletableFuture에 대한 설명

자바에서 멀티스레드 프로그래밍을 지원하는 주요 인터페이스와 클래스는 Runnable, Callable, Future, 그리고 CompletableFuture입니다. 이들은 작업을 정의하고, 실행하며, 결과를 처리하는 데 사용됩니다. 아래에서 각 클래스와 관련 메서드를 표로 정리하고, 예시를 통해 사용 방법을 설명하겠습니다.


관련 클래스 및 메서드 표

Runnable 인터페이스

메서드명 반환 타입 설명
run() void 스레드가 실행할 작업을 정의 (추상 메서드)

Callable<V> 인터페이스

메서드명 반환 타입 설명
call() V 결과를 반환하며 예외를 던질 수 있는 작업 정의

Future<V> 인터페이스

메서드명 반환 타입 설명
get() V 작업 결과를 반환 (완료될 때까지 대기)
get(long, TimeUnit) V 지정된 시간 동안 대기 후 결과 반환
cancel(boolean) boolean 작업을 취소 시도하고 성공 여부 반환
isDone() boolean 작업이 완료되었는지 확인
isCancelled() boolean 작업이 취소되었는지 확인

CompletableFuture<V> 클래스

메서드명 반환 타입 설명
runAsync(Runnable) CompletableFuture<Void> 비동기적으로 Runnable 실행
supplyAsync(Supplier<V>) CompletableFuture<V> 비동기적으로 값을 제공하는 작업 실행
thenApply(Function<T, U>) CompletableFuture<U> 이전 작업의 결과에 함수를 적용하여 변환된 결과 반환
thenAccept(Consumer<T>) CompletableFuture<Void> 결과에 대한 소비 동작 수행
thenRun(Runnable) CompletableFuture<Void> 결과와 상관없이 후속 작업 실행
exceptionally(Function<Throwable, T>) CompletableFuture<T> 예외 발생 시 처리 로직 정의
complete(V) boolean 작업을 수동으로 완료하고 결과 설정
join() V 작업 완료를 기다리고 결과 반환 (스레드 풀에서 사용 시 주의)
allOf(CompletableFuture<?>...) CompletableFuture<Void> 여러 CompletableFuture가 모두 완료될 때까지 대기
anyOf(CompletableFuture<?>...) CompletableFuture<Object> 여러 CompletableFuture 중 하나가 완료되면 결과 반환

설명 및 예시

이들 클래스는 멀티스레드 작업의 정의와 실행, 결과 처리를 다룹니다. Runnable은 기본적인 작업 단위이고, Callable은 결과를 반환하며, Future는 결과를 추적하고, CompletableFuture는 비동기 작업의 조합과 예외 처리를 지원합니다.

1. Runnable: 기본 작업 정의

Runnable은 결과를 반환하지 않는 간단한 작업을 정의합니다.

public class RunnableExample {
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println("Runnable 작업 실행: " + Thread.currentThread().getName());
        };

        Thread thread = new Thread(task);
        thread.start();
    }
}
  • 출력 예시:
    Runnable 작업 실행: Thread-0
    

2. CallableFuture: 결과 반환

Callable은 값을 반환하며, Future로 결과를 받습니다.

import java.util.concurrent.*;

public class CallableFutureExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        Callable<String> task = () -> {
            Thread.sleep(1000);
            return "Callable 작업 완료";
        };

        Future<String> future = executor.submit(task);
        System.out.println("작업 제출 완료");

        String result = future.get(); // 완료 대기
        System.out.println(result);

        executor.shutdown();
    }
}
  • 출력 예시:
    작업 제출 완료
    (1초 후)
    Callable 작업 완료
    
  • 설명: Future.get()은 작업이 끝날 때까지 대기하며 결과를 반환.

3. CompletableFuture: 비동기 작업 조합

CompletableFuture는 비동기 작업을 연결하고 결과를 처리하는 데 유용합니다.

import java.util.concurrent.*;

public class CompletableFutureExample {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "비동기 결과";
        }).thenApply(result -> result + " 처리됨")
          .thenApply(result -> result.toUpperCase());

        String result = future.get();
        System.out.println(result);
    }
}
  • 출력 예시:
    (1초 후)
    비동기 결과 처리됨
    
  • 설명: supplyAsync로 값을 생성하고, thenApply로 결과를 변환.

4. CompletableFuture 예외 처리 및 조합

예외 처리와 여러 작업의 동시 실행을 다룹니다.

import java.util.concurrent.*;

public class CompletableFutureAdvancedExample {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "작업 1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            throw new RuntimeException("작업 2 실패");
        }).exceptionally(throwable -> "복구: " + throwable.getMessage());

        CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
        all.thenRun(() -> {
            try {
                System.out.println(future1.get() + " & " + future2.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).join(); // 모든 작업 완료 대기
    }
}
  • 출력 예시:
    작업 1 & 복구: java.lang.RuntimeException: 작업 2 실패
    
  • 설명: exceptionally로 예외를 처리하고, allOf로 여러 작업을 조합.

5. CompletableFuture로 수동 완료

complete를 사용해 작업을 수동으로 완료할 수 있습니다.

import java.util.concurrent.*;

public class ManualCompleteExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = new CompletableFuture<>();

        new Thread(() -> {
            try {
                Thread.sleep(1000);
                future.complete("수동 완료");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        String result = future.join();
        System.out.println(result);
    }
}
  • 출력 예시:
    (1초 후)
    수동 완료
    

비교와 특징

  • Runnable: 결과 없음, 단순 작업 실행.
  • Callable: 결과 반환, Future와 함께 사용.
  • Future: 비동기 작업 상태 추적, 블록킹 방식.
  • CompletableFuture: 비동기 작업 조합, 예외 처리, 논블록킹 지원.

결론

RunnableCallable은 작업을 정의하고, Future는 결과를 추적하며, CompletableFuture는 복잡한 비동기 작업을 유연하게 처리합니다. 상황에 따라 적합한 클래스를 선택해 사용하면 효율적인 멀티스레드 프로그래밍이 가능합니다. 위 예시를 참고하여 실제 애플리케이션에 적용해 보세요!