IT

JAVA에서 비동기 처리: Future, CompletableFuture, 그리고 효율적인 비동기 스트림 처리 방법

경제향기✨ 2024. 6. 26.

비동기 처리(Asynchronous Processing)는 Java에서 중요한 개념 중 하나로, 특히 고성능 애플리케이션을 개발할 때 필수적인 기술입니다. 비동기 처리를 통해 애플리케이션은 특정 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속 수행할 수 있습니다. 이는 응답성을 높이고, 자원을 효율적으로 사용하며, 전체 처리 속도를 향상시킵니다. 이 블로그 포스트에서는 Java에서 비동기 처리를 구현하는 다양한 방법과 예시 코드를 소개하겠습니다.

JAVA에서 비동기 처리: Future, CompletableFuture, 그리고 효율적인 비동기 스트림 처리 방법

1. 비동기 처리의 기본 개념

비동기 처리에서는 작업이 완료될 때까지 기다리지 않고, 작업이 완료되면 이를 처리할 수 있는 메커니즘이 필요합니다. Java에서는 이를 위해 주로 Future, CompletableFuture, 그리고 ExecutorService를 사용합니다.

1.1 Future와 ExecutorService

Future는 비동기 작업의 결과를 나타내며, ExecutorService는 비동기 작업을 실행하기 위한 스레드 풀을 제공합니다.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        Callable<String> callableTask = () -> {
            Thread.sleep(2000);
            return "Task's execution";
        };

        Future<String> future = executor.submit(callableTask);

        while (!future.isDone()) {
            System.out.println("Task is still not done...");
            try {
                Thread.sleep(500); // 잠시 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            System.out.println("Task completed! Retrieving the result");
            String result = future.get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

위 코드에서는 Callable 인터페이스를 사용하여 비동기 작업을 정의하고, ExecutorService를 통해 해당 작업을 실행합니다. Future 객체를 통해 작업의 완료 상태를 확인하고, 결과를 가져옵니다.

1.2 CompletableFuture

CompletableFuture는 Future를 확장한 것으로, 더 강력한 기능과 다양한 콜백 메서드를 제공합니다. 이를 통해 비동기 작업을 더욱 효율적으로 처리할 수 있습니다.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello, World!";
        });

        future.thenAccept(result -> System.out.println("Result: " + result))
              .exceptionally(ex -> {
                  System.out.println("Exception: " + ex);
                  return null;
              });

        // 다른 작업 수행 가능
        System.out.println("Doing other tasks...");

        try {
            // 미래의 완료를 기다림
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

위 코드에서는 CompletableFuture.supplyAsync 메서드를 사용하여 비동기 작업을 정의하고, 작업이 완료되면 thenAccept 메서드를 통해 결과를 처리합니다. 또한 exceptionally 메서드를 사용하여 예외를 처리할 수 있습니다.

1.3 비동기 스트림 처리

Java 8의 스트림 API와 CompletableFuture를 결합하여 비동기 스트림 처리를 할 수 있습니다.

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class AsyncStreamExample {
    public static void main(String[] args) {
        List<CompletableFuture<String>> futures = List.of("task1", "task2", "task3").stream()
                .map(task -> CompletableFuture.supplyAsync(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "Result of " + task;
                }))
                .collect(Collectors.toList());

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

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

위 코드에서는 여러 비동기 작업을 스트림으로 처리하여 각 작업의 결과를 리스트로 수집합니다. join 메서드를 사용하여 모든 비동기 작업이 완료될 때까지 기다립니다.

결론

Java에서 비동기 처리를 통해 애플리케이션의 성능을 크게 향상시킬 수 있습니다. Future와 ExecutorService를 사용하여 기본적인 비동기 작업을 처리하고, CompletableFuture를 통해 더 복잡한 비동기 작업을 효율적으로 처리할 수 있습니다. 또한 스트림 API를 사용하여 비동기 작업을 쉽게 결합할 수 있습니다. 위에서 소개한 예제 코드를 활용하여 다양한 비동기 처리 패턴을 학습하고 적용해보세요.

댓글

💲 추천 글