1️⃣ CompletableFuture란?
- 비동기 작업(Async Task)을 쉽게 관리할 수 있는 클래스
- 콜백(Callback) 기반으로 동작하며, 작업 완료 후 후속 처리를 간편하게 정의 가능
- Future의 단점을 해결하여, 결과를 기다리지 않고도 다음 작업을 수행할 수 있다.
2️⃣ Future vs CompletableFuture 차이
기존 Future의 문제점
기존 Future 객체는 비동기 작업을 실행할 수 있지만 제한점이 많음
더보기
import java.util.concurrent.*;
public class FutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> future = executor.submit(() -> {
Thread.sleep(2000); // 2초 대기
return "작업 완료!";
});
System.out.println("Future 작업 실행 중...");
// 🔹 `get()`을 호출하면 결과를 받을 때까지 블로킹됨 (문제점)
String result = future.get(); // 결과를 기다림
System.out.println("결과: " + result);
executor.shutdown();
}
}
Future의 문제점
- get()을 호출하면 결과가 나올 때까지 기다려야 함 (Blocking)
- 작업 완료 후 후속 처리(thenApply 같은 체이닝 기능)가 없음
- 여러 개의 Future를 조합하는 것이 불편함
3️⃣ CompletableFuture 기본 사용법
CompletableFuture를 사용하면 콜백 기반으로 작업을 연결(Chaining)할 수 있어 더욱 유연하다
기본적인 비동기 실행 (runAsync vs supplyAsync)
더보기
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
// 반환 값이 없는 비동기 작업
CompletableFuture.runAsync(() -> {
System.out.println("비동기 작업 실행 중... " + Thread.currentThread().getName());
});
// 반환 값이 있는 비동기 작업
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "비동기 작업 결과!";
});
System.out.println("메인 스레드는 계속 실행됨!");
// 결과를 기다림 (하지만 `Future`보다 훨씬 더 유연하게 사용 가능)
System.out.println("결과: " + future.join()); // `join()`을 사용하면 블로킹 없이 결과를 가져올 수 있음
}
}
✅ runAsync() → 반환 값이 필요 없을 때 사용
✅ supplyAsync() → 비동기 작업의 결과값을 받을 때 사용
4️⃣ CompletableFuture 체이닝 (Chaining)
비동기 작업이 끝난 후 다음 작업을 실행하는 방법
thenApply() (결과를 변환)
더보기
import java.util.concurrent.CompletableFuture;
public class CompletableFutureChaining {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(result -> result + " World") // 결과 변환
.thenApply(result -> result.toUpperCase()) // 대문자로 변환
.thenAccept(System.out::println); // 최종 결과 출력
}
}
✅ thenApply() → 이전 작업의 결과를 받아서 변환한 후 반환
5️⃣ thenCompose() vs thenCombine() (비동기 작업 조합)
여러 개의 CompletableFuture를 조합해서 실행하는 방법
thenCompose() - 이전 결과를 기반으로 새로운 비동기 작업 실행
더보기
import java.util.concurrent.CompletableFuture;
public class CompletableFutureThenCompose {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> "User ID: 123")
.thenCompose(id -> getUserDetails(id)) // 새로운 비동기 작업 실행
.thenAccept(System.out::println);
}
// 🔹 유저 정보를 가져오는 비동기 메서드
public static CompletableFuture<String> getUserDetails(String userId) {
return CompletableFuture.supplyAsync(() -> userId + " (홍길동, 29세)");
}
}
✅ thenCompose() → 이전 결과를 이용하여 새로운 CompletableFuture 실행
thenCombine() - 두 개의 비동기 작업을 병렬로 실행한 후 결과 합치기
더보기
import java.util.concurrent.CompletableFuture;
public class CompletableFutureThenCombine {
public static void main(String[] args) {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
// 두 개의 비동기 작업을 병렬로 실행한 후 결과를 조합
future1.thenCombine(future2, (result1, result2) -> result1 + " " + result2)
.thenAccept(System.out::println);
}
}
6️⃣ 예외 처리 (exceptionally, handle)
비동기 작업에서 예외가 발생했을 때 처리하는 방법
exceptionally() - 예외 발생 시 기본값 반환
더보기
import java.util.concurrent.CompletableFuture;
public class CompletableFutureException {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("에러 발생!");
return "정상 실행";
}).exceptionally(ex -> {
System.out.println("예외 처리: " + ex.getMessage());
return "기본값";
}).thenAccept(System.out::println);
}
}
handle() - 정상 / 예외 모두 처리 가능
더보기
import java.util.concurrent.CompletableFuture;
public class CompletableFutureHandle {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("에러 발생!");
return "정상 실행";
}).handle((result, ex) -> {
if (ex != null) {
System.out.println("예외 처리: " + ex.getMessage());
return "기본값";
}
return result;
}).thenAccept(System.out::println);
}
}
handle() → 정상적인 결과와 예외를 모두 처리할 수 있음
CompletableFuture 정리
| 기능 | 메서드 |
| 비동기 작업 실행 | runAsync(), supplyAsync() |
| 후속 작업 추가 | thenApply(), thenAccept() |
| 비동기 작업 조합 | thenCompose(), thenCombine() |
| 예외 처리 | exceptionally(), handle() |
'JAVA' 카테고리의 다른 글
| Java Map(맵) (2) | 2025.03.12 |
|---|---|
| 가상 쓰레드 (Virtual Threads) (0) | 2025.03.12 |
| Executor 프레임워크 (0) | 2025.03.12 |
| 쓰레드 동기화 (Synchronization) (3) | 2025.03.11 |
| 쓰레드(Thread) (0) | 2025.03.11 |