본문 바로가기
Java

[JAVA] Callable과 Future로 비동기 작업 이해하기

by teamnova 2025. 4. 3.
728x90

비동기 프로그래밍은 현대 애플리케이션 개발에서 필수적인 기술입니다. 자바에서는 비동기 작업을 효율적으로 처리하기 위해 Callable과 Future라는 인터페이스를 제공합니다. 이 글에서는 비동기의 개념부터 Callable과 Future를 활용한 실질적인 예제까지 차근차근 설명하겠습니다.

비동기란 무엇인가?

비동기 작업이란 작업을 실행한 후 해당 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있는 방식을 말합니다. 이는 멀티쓰레드 환경에서 특히 유용하며, 다음과 같은 특징을 갖습니다:

  • 병렬 처리: 여러 작업을 동시에 실행하여 처리 속도를 높임.
  • 작업 대기 최소화: 작업 완료를 기다리지 않고 다른 작업을 수행 가능.
  • 효율성: 시스템 자원을 효율적으로 활용.

자바에서는 이러한 비동기 작업을 구현하기 위해 Runnable, Callable, Future와 같은 도구들을 제공합니다.

Callable과 Future란 무엇인가?

Callable

  • Callable은 자바의 인터페이스로, 비동기 작업을 수행하고 결과를 반환할 수 있습니다.
  • Runnable과 달리, 작업이 완료된 후 값을 반환하거나 예외를 던질 수 있습니다.
  • 주요 메서드
    • call() : 작업을 수행하고 결과를 반환합니다.

Future

  • Future는 비동기 작업의 결과를 저장하는 컨테이너 역할을 합니다.
  • 작업이 완료되기 전까지 결과를 얻으려고 하면 대기 상태가 됩니다.
  • 주요 메서드:
    • get(): 작업 완료 후 결과를 반환. 작업이 완료되지 않았다면 대기.
    • isDone(): 작업 완료 여부를 반환.
    • cancel(): 작업을 취소.

Callable과 Runnable의 차이점

특징 Runnable Callable
메서드 run() call()
결과 반환 없음 있음
예외 처리 예외 처리 불가능 예외 처리 가능
사용 목적 단순 작업 실행 작업 실행 후 결과 반환 및 예외 처리

Callable과 Future 사용 방법

Callable과 Future를 사용하려면 ExecutorService를 활용하여 비동기 작업을 실행하고 결과를 관리할 수 있습니다. 아래에서 단계별로 설명하겠습니다.

1. Callable 구현하기

Callable 인터페이스를 구현하여 작업을 정의합니다.

import java.util.concurrent.Callable;

// Callable 구현 클래스
public class SumTask implements Callable<Integer> {
    private int a;
    private int b;

    public SumTask(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call() throws Exception {
        // 두 숫자의 합을 계산하고 반환
        return a + b;
    }
}

 

2. ExecutorService를 사용해 Callable 실행하기

ExecutorService를 통해 Callable 작업을 실행하고, 결과를 Future로 관리합니다.

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

public class CallableExample {
    public static void main(String[] args) throws Exception {
        // ExecutorService 생성 (스레드 풀 크기: 2)
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // Callable 작업 생성
        SumTask task = new SumTask(5, 10);

        // Callable 작업 제출하고 Future 객체 반환받음
        Future<Integer> future = executor.submit(task);

        // 작업 완료 여부 확인
        if (!future.isDone()) {
            System.out.println("작업 진행 중...");
        }

        // 작업 결과 가져오기
        Integer result = future.get(); // 작업이 완료될 때까지 대기
        System.out.println("작업 결과: " + result);

        // ExecutorService 종료
        executor.shutdown();
    }
}

 

3. 여러 Callable 작업 실행하기

여러 Callable 작업을 동시에 실행하고 결과를 처리하는 방법입니다.

import java.util.concurrent.*;

public class MultiCallableExample {
    public static void main(String[] args) throws Exception {
        // ExecutorService 생성
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 여러 Callable 작업 생성
        Callable<Integer> task1 = () -> {
            Thread.sleep(1000);
            return 10;
        };

        Callable<Integer> task2 = () -> {
            Thread.sleep(2000);
            return 20;
        };

        Callable<Integer> task3 = () -> {
            Thread.sleep(3000);
            return 30;
        };

        // 작업 제출
        Future<Integer> future1 = executor.submit(task1);
        Future<Integer> future2 = executor.submit(task2);
        Future<Integer> future3 = executor.submit(task3);

        // 결과 가져오기
        System.out.println("Task1 결과: " + future1.get());
        System.out.println("Task2 결과: " + future2.get());
        System.out.println("Task3 결과: " + future3.get());

        // ExecutorService 종료
        executor.shutdown();
    }
}

 

Callable과 Future 활용 사례

1. 파일 다운로드

여러 파일을 동시에 다운로드하면서 다운로드 완료 후 파일 크기를 반환하는 작업에 활용할 수 있습니다.

2. 데이터베이스 쿼리 처리

비동기적으로 여러 쿼리를 실행하고 결과를 반환받는 데 사용할 수 있습니다.

3. 계산 작업

복잡한 계산 작업을 여러 쓰레드로 나누어 실행한 후 결과를 통합하는 데 적합합니다.

 

Callable과 Future는 자바에서 비동기 작업을 구현하는 강력한 도구입니다. Runnable보다 더 많은 기능을 제공하며, 작업 결과를 반환하거나 예외를 처리할 수 있습니다. 실무에서 비동기 작업을 효율적으로 처리하려면 Callable과 Future를 잘 활용하는 것이 중요합니다.