### 자바의 `Runnable`, `Callable`, `Future`, `CompletableFuture`에 대한 설명 자바에서 멀티스레드 프로그래밍을 지원하는 주요 인터페이스와 클래스는 `Runnable`, `Callable`, `Future`, 그리고 `CompletableFuture`입니다. 이들은 작업을 정의하고, 실행하며, 결과를 처리하는 데 사용됩니다. 아래에서 각 클래스와 관련 메서드를 표로 정리하고, 예시를 통해 사용 방법을 설명하겠습니다. --- ### 관련 클래스 및 메서드 표 #### `Runnable` 인터페이스 | 메서드명 | 반환 타입 | 설명 | |-----------------|-----------|---------------------------------------------------| | `run()` | `void` | 스레드가 실행할 작업을 정의 (추상 메서드) | #### `Callable` 인터페이스 | 메서드명 | 반환 타입 | 설명 | |-----------------|-----------|---------------------------------------------------| | `call()` | `V` | 결과를 반환하며 예외를 던질 수 있는 작업 정의 | #### `Future` 인터페이스 | 메서드명 | 반환 타입 | 설명 | |-------------------------|-----------|---------------------------------------------------| | `get()` | `V` | 작업 결과를 반환 (완료될 때까지 대기) | | `get(long, TimeUnit)` | `V` | 지정된 시간 동안 대기 후 결과 반환 | | `cancel(boolean)` | `boolean` | 작업을 취소 시도하고 성공 여부 반환 | | `isDone()` | `boolean` | 작업이 완료되었는지 확인 | | `isCancelled()` | `boolean` | 작업이 취소되었는지 확인 | #### `CompletableFuture` 클래스 | 메서드명 | 반환 타입 | 설명 | |-----------------------------------|-------------------------|----------------------------------------------------------------------| | `runAsync(Runnable)` | `CompletableFuture` | 비동기적으로 `Runnable` 실행 | | `supplyAsync(Supplier)` | `CompletableFuture` | 비동기적으로 값을 제공하는 작업 실행 | | `thenApply(Function)` | `CompletableFuture` | 이전 작업의 결과에 함수를 적용하여 변환된 결과 반환 | | `thenAccept(Consumer)` | `CompletableFuture` | 결과에 대한 소비 동작 수행 | | `thenRun(Runnable)` | `CompletableFuture` | 결과와 상관없이 후속 작업 실행 | | `exceptionally(Function)` | `CompletableFuture` | 예외 발생 시 처리 로직 정의 | | `complete(V)` | `boolean` | 작업을 수동으로 완료하고 결과 설정 | | `join()` | `V` | 작업 완료를 기다리고 결과 반환 (스레드 풀에서 사용 시 주의) | | `allOf(CompletableFuture...)` | `CompletableFuture` | 여러 `CompletableFuture`가 모두 완료될 때까지 대기 | | `anyOf(CompletableFuture...)` | `CompletableFuture` | 여러 `CompletableFuture` 중 하나가 완료되면 결과 반환 | --- ### 설명 및 예시 이들 클래스는 멀티스레드 작업의 정의와 실행, 결과 처리를 다룹니다. `Runnable`은 기본적인 작업 단위이고, `Callable`은 결과를 반환하며, `Future`는 결과를 추적하고, `CompletableFuture`는 비동기 작업의 조합과 예외 처리를 지원합니다. #### 1. `Runnable`: 기본 작업 정의 `Runnable`은 결과를 반환하지 않는 간단한 작업을 정의합니다. ```java 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. `Callable`과 `Future`: 결과 반환 `Callable`은 값을 반환하며, `Future`로 결과를 받습니다. ```java import java.util.concurrent.*; public class CallableFutureExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Callable task = () -> { Thread.sleep(1000); return "Callable 작업 완료"; }; Future future = executor.submit(task); System.out.println("작업 제출 완료"); String result = future.get(); // 완료 대기 System.out.println(result); executor.shutdown(); } } ``` - **출력 예시**: ``` 작업 제출 완료 (1초 후) Callable 작업 완료 ``` - **설명**: `Future.get()`은 작업이 끝날 때까지 대기하며 결과를 반환. #### 3. `CompletableFuture`: 비동기 작업 조합 `CompletableFuture`는 비동기 작업을 연결하고 결과를 처리하는 데 유용합니다. ```java import java.util.concurrent.*; public class CompletableFutureExample { public static void main(String[] args) throws Exception { CompletableFuture 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` 예외 처리 및 조합 예외 처리와 여러 작업의 동시 실행을 다룹니다. ```java import java.util.concurrent.*; public class CompletableFutureAdvancedExample { public static void main(String[] args) throws Exception { CompletableFuture future1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } return "작업 1"; }); CompletableFuture future2 = CompletableFuture.supplyAsync(() -> { throw new RuntimeException("작업 2 실패"); }).exceptionally(throwable -> "복구: " + throwable.getMessage()); CompletableFuture 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`를 사용해 작업을 수동으로 완료할 수 있습니다. ```java import java.util.concurrent.*; public class ManualCompleteExample { public static void main(String[] args) { CompletableFuture 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`**: 비동기 작업 조합, 예외 처리, 논블록킹 지원. --- ### 결론 `Runnable`과 `Callable`은 작업을 정의하고, `Future`는 결과를 추적하며, `CompletableFuture`는 복잡한 비동기 작업을 유연하게 처리합니다. 상황에 따라 적합한 클래스를 선택해 사용하면 효율적인 멀티스레드 프로그래밍이 가능합니다. 위 예시를 참고하여 실제 애플리케이션에 적용해 보세요!