Комментарии (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
Ключевые различия
| Аспект | Callable | Future |
|---|---|---|
| Тип | Interface для task | Interface для результата |
| Метод | call() | get(), isDone(), cancel() |
| Возвращает | Значение типа V | Future<V> |
| Исключения | Может выбросить checked | Оборачивает в ExecutionException |
| Запуск | Через executor.submit() | Не запускается, только получает результат |
| Блокировка | Не блокирует | get() блокирует |
Вывод
Callable и Future — это пара для асинхронного программирования:
- Callable — описание task'а, что нужно сделать
- Future — способ получить результат этого task'а
Связь: executor.submit(Callable) → Future
Используй когда:
- Нужен результат из другого потока
- Нужна контроль над отменой (cancel)
- Нужно обработать исключения из другого потока
- Для асинхронных операций в Spring сервисах
Альтернативы: CompletableFuture (Java 8+), ReactiveStreams, Project Reactor