← Назад к вопросам

Как связаны Callable и Future?

1.8 Middle🔥 131 комментариев
#ORM и Hibernate

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как связаны Callable и Future

Callable и Future тесно связаны: Callable — это task для выполнения, Future — это результат этого task'а.

1. Базовые определения

Callable

public interface Callable<V> {
    V call() throws Exception;
}

Callable — это функциональный интерфейс, который возвращает результат и может выбросить исключение.

Отличие от Runnable:

public interface Runnable {
    void run();  // Не возвращает результат, не выбрасывает checked exception
}

Future

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

Future представляет результат асинхронной операции, который может быть ещё не готов.

2. Связь между Callable и Future

Callable (task) → ExecutorService → Future (результат)
     ↓                                     ↓
  call()                           get() для получения результата

3. Полный пример

public class CallableVsFutureExample {
    
    // Определяем Callable task
    static class CalculationTask implements Callable<Integer> {
        private int n;
        
        public CalculationTask(int n) {
            this.n = n;
        }
        
        @Override
        public Integer call() throws Exception {
            // Это выполняется в отдельном потоке
            System.out.println("Calculating factorial of " + n);
            Thread.sleep(2000);  // Имитируем долгую операцию
            
            int result = 1;
            for (int i = 1; i <= n; i++) {
                result *= i;
            }
            return result;
        }
    }
    
    public static void main(String[] args) throws Exception {
        // Создаем ExecutorService
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        // Подаём Callable task и получаем Future
        Future<Integer> future = executor.submit(new CalculationTask(5));
        
        // Main поток может продолжить работу (не блокируется)
        System.out.println("Main thread continues...");
        
        // Позже проверяем статус
        if (!future.isDone()) {
            System.out.println("Task still running...");
        }
        
        // Когда нужен результат — вызываем get()
        Integer result = future.get();  // БЛОКИРУЕТ до завершения
        System.out.println("Result: " + result);
        
        executor.shutdown();
    }
}

Вывод:
Main thread continues...
Calculating factorial of 5
Task still running...
(ждем 2 секунды)
Result: 120

4. Разница с Runnable + Thread

С Runnable (старый способ)

Runnable runnable = () -> {
    System.out.println("Task running");
    // Нельзя вернуть результат
};

Thread thread = new Thread(runnable);
thread.start();
thread.join();  // Ждём завершения
// Нет способа получить результат!

С Callable (новый способ)

Callable<String> callable = () -> {
    System.out.println("Task running");
    return "Result";  // Можем вернуть результат
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callable);
String result = future.get();  // Получаем результат

5. Работа с Future методами

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

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(task);

// 1. Проверить, готов ли результат
if (!future.isDone()) {
    System.out.println("Still processing...");
}

// 2. Получить результат с timeout'ом
try {
    Integer result = future.get(5, TimeUnit.SECONDS);  // Максимум 5 сек
    System.out.println("Result: " + result);
} catch (TimeoutException e) {
    System.out.println("Task took too long");
}

// 3. Отменить task (если он ещё не начал выполняться)
future.cancel(true);  // true = прервать, если выполняется
if (future.isCancelled()) {
    System.out.println("Task was cancelled");
}

6. Множественные Callable tasks

List<Callable<Integer>> tasks = Arrays.asList(
    () -> { Thread.sleep(1000); return 1; },
    () -> { Thread.sleep(2000); return 2; },
    () -> { Thread.sleep(500); return 3; }
);

ExecutorService executor = Executors.newFixedThreadPool(3);

// Вариант 1: invokeAll (ждёт все)
List<Future<Integer>> futures = executor.invokeAll(tasks);
for (Future<Integer> future : futures) {
    System.out.println("Result: " + future.get());  // БЛОКИРУЕТ
}

// Вариант 2: invokeAny (ждёт первый)
Integer firstResult = executor.invokeAny(tasks);  // Возвращает первый готовый результат
System.out.println("First result: " + firstResult);  // 3 (самый быстрый)

7. Обработка исключений

Callable<Integer> failingTask = () -> {
    throw new IOException("Something went wrong");
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(failingTask);

try {
    Integer result = future.get();  // Исключение будет выброшено здесь
} catch (ExecutionException e) {
    // e.getCause() содержит исходное исключение
    System.out.println("Task failed: " + e.getCause().getMessage());
} catch (InterruptedException e) {
    System.out.println("Waiting was interrupted");
}

8. CompletableFuture (улучшение)

В Java 8+ есть CompletableFuture для более удобной работы:

// Старый способ с Future
Future<Integer> future = executor.submit(() -> 10);
Integer result = future.get();  // БЛОКИРУЕТ

// Новый способ с CompletableFuture
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(() -> 10);
cf.thenApply(x -> x * 2)           // 20
  .thenApply(x -> x + 5)           // 25
  .thenAccept(System.out::println); // Выводит результат

// Не блокирует!

9. Практический пример: Загрузка данных

@Service
public class DataService {
    private final ExecutorService executor = Executors.newFixedThreadPool(5);
    
    public Future<List<User>> loadUsersAsync(List<Long> userIds) {
        return executor.submit(() -> {
            List<User> users = new ArrayList<>();
            for (Long id : userIds) {
                users.add(loadUserFromDb(id));  // Долгая операция
            }
            return users;
        });
    }
    
    public void processUsers(List<Long> userIds) throws Exception {
        Future<List<User>> future = loadUsersAsync(userIds);
        
        // Может что-то делать в Main потоке
        System.out.println("Loading users...");
        
        // Когда нужны результаты
        List<User> users = future.get(30, TimeUnit.SECONDS);
        System.out.println("Got " + users.size() + " users");
    }
}

10. Stream + Callable + Future

List<String> urls = Arrays.asList("url1", "url2", "url3");
ExecutorService executor = Executors.newFixedThreadPool(3);

// Создаём Callable для каждого URL
List<Callable<String>> tasks = urls.stream()
    .map(url -> (Callable<String>) () -> downloadContent(url))
    .collect(Collectors.toList());

// Подаём все task'и
List<Future<String>> futures = executor.invokeAll(tasks);

// Собираем результаты
List<String> results = futures.stream()
    .map(future -> {
        try {
            return future.get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    })
    .collect(Collectors.toList());

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

Диаграмма жизненного цикла

Callable submitted
    ↓
Future returned
    ↓
Task executing (isDone = false)
    ↓
Task completed / failed / cancelled
    ↓
Future.get() возвращает результат / исключение
    ↓
isDone = true

Ключевые различия

АспектCallableFuture
ТипInterface для taskInterface для результата
Методcall()get(), isDone(), cancel()
ВозвращаетЗначение типа VFuture<V>
ИсключенияМожет выбросить checkedОборачивает в ExecutionException
ЗапускЧерез executor.submit()Не запускается, только получает результат
БлокировкаНе блокируетget() блокирует

Вывод

Callable и Future — это пара для асинхронного программирования:

  • Callable — описание task'а, что нужно сделать
  • Future — способ получить результат этого task'а

Связь: executor.submit(Callable) → Future

Используй когда:

  • Нужен результат из другого потока
  • Нужна контроль над отменой (cancel)
  • Нужно обработать исключения из другого потока
  • Для асинхронных операций в Spring сервисах

Альтернативы: CompletableFuture (Java 8+), ReactiveStreams, Project Reactor

Как связаны Callable и Future? | PrepBro