IT/JAVA

Java 멀티쓰레드 프로그래밍: 비동기 프로그래밍과 CompletableFuture 마스터하기

KeepGooing 2024. 12. 18. 13:52
반응형

1. CompletableFuture 체이닝과 조합

복잡한 비동기 작업 흐름을 구성하는 방법을 알아봅시다.


public class AsyncOperations {
    public CompletableFuture fetchUserData(String userId) {
        return CompletableFuture.supplyAsync(() -> {
            // 사용자 데이터 비동기 조회
            return "User data for " + userId;
        });
    }

    public CompletableFuture processUserData(String userData) {
        return CompletableFuture.supplyAsync(() -> {
            // 사용자 데이터 처리
            return "Processed: " + userData;
        });
    }

    public CompletableFuture notifyUser(String processedData) {
        return CompletableFuture.supplyAsync(() -> {
            // 사용자 알림
            return "Notified user with: " + processedData;
        });
    }

    public CompletableFuture completeUserFlow(String userId) {
        return fetchUserData(userId)
                .thenCompose(this::processUserData)
                .thenCompose(this::notifyUser)
                .exceptionally(ex -> "Error: " + ex.getMessage());
    }
}
        

2. 커스텀 Executor 활용

CompletableFuture와 함께 사용자 정의 Executor를 활용하여 성능을 최적화합니다.


public class CustomExecutorExample {
    private static final Executor CUSTOM_EXECUTOR = new ThreadPoolExecutor(
        10, 50, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100),
        new ThreadPoolExecutor.CallerRunsPolicy());

    public CompletableFuture performTask(String input) {
        return CompletableFuture.supplyAsync(() -> {
            // 시간이 걸리는 작업 수행
            return "Result: " + input;
        }, CUSTOM_EXECUTOR);
    }

    public void executeMultipleTasks() {
        List inputs = Arrays.asList("Task1", "Task2", "Task3");
        List<CompletableFuture> futures = inputs.stream()
            .map(this::performTask)
            .collect(Collectors.toList());

        CompletableFuture allOf = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture));

        allOf.join(); // 모든 작업이 완료될 때까지 대기

        List results = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());

        results.forEach(System.out::println);
    }
}
        

3. 고급 에러 처리 전략

비동기 작업에서의 효과적인 에러 처리 방법을 알아봅니다.


public class AdvancedErrorHandling {
    public CompletableFuture reliableAsyncOperation() {
        return CompletableFuture.supplyAsync(() -> {
            if (Math.random() < 0.5) throw new RuntimeException("Random failure");
            return "Success";
        }).handle((result, ex) -> {
            if (ex != null) {
                logger.error("Operation failed", ex);
                return "Fallback result";
            }
            return result;
        }).thenApply(result -> {
            // 추가 처리
            return "Processed: " + result;
        }).orTimeout(5, TimeUnit.SECONDS)
          .exceptionally(ex -> {
              if (ex instanceof TimeoutException) {
                  return "Operation timed out";
              }
              return "Unexpected error: " + ex.getMessage();
          });
    }
}
        

결론

CompletableFuture와 비동기 프로그래밍 기법을 마스터하면 복잡한 비동기 작업 흐름을 효과적으로 관리할 수 있습니다. 커스텀 Executor 활용, 체이닝과 조합, 그리고 고급 에러 처리 전략을 통해 더욱 견고하고 효율적인 비동기 애플리케이션을 개발할 수 있습니다.

비동기 프로그래밍은 현대 애플리케이션 개발에서 필수적인 기술입니다. CompletableFuture의 고급 기능을 지속적으로 학습하고 실제 프로젝트에 적용해 보세요.

반응형