Отправка задач: Runnable и Callable
Отправляйте задачи Runnable и Callable и получайте результаты через Future.
«Отправка задач: Runnable и Callable» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Задачи без результата и с результатом
Два функциональных интерфейса представляют задачи, отправляемые в ExecutorService:
- Задача без результата: не возвращает значение и не может объявлять проверяемые исключения
- Задача с результатом: возвращает результат типа V и может выбрасывать проверяемые исключения
import java.util.concurrent.*;
// Runnable — no return value
Runnable r = () -> System.out.println("Running");
// Callable<Integer> — returns a value
Callable<Integer> c = () -> {
return 42; // or do computation
};submit для задачи без результата
submit(Runnable) возвращает объект Future<?>. Вызов get() для него возвращает null, но подтверждает завершение задачи (или выбрасывает исключение, если задача завершилась с ошибкой).
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<?> future = pool.submit(() ->
System.out.println("Task done on " + Thread.currentThread().getName())
);
future.get(); // waits for completion; returns null
System.out.println("Task confirmed complete");
pool.shutdown();submit для задачи с результатом
submit(Callable) возвращает объект Future<V>. Вызовите future.get(), чтобы получить результат (вызов блокирует выполнение до завершения задачи).
ExecutorService pool = Executors.newFixedThreadPool(2);
Callable<Integer> sumTask = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
return sum;
};
Future<Integer> future = pool.submit(sumTask);
Integer result = future.get(); // blocks until done
System.out.println("Sum 1..100 = " + result); // 5050
pool.shutdown();Отправка нескольких задач с результатом с помощью invokeAll
invokeAll() отправляет коллекцию задач с результатом и возвращает список объектов Future — к моменту возврата все задачи завершены:
List<Callable<String>> tasks = List.of(
() -> "Result A",
() -> "Result B",
() -> "Result C"
);
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<String>> futures = pool.invokeAll(tasks);
for (Future<String> f : futures) {
System.out.println(f.get());
}
pool.shutdown();invokeAny: первый успешный результат
invokeAny() отправляет коллекцию задач с результатом и возвращает результат первой успешно завершившейся задачи, отменяя остальные:
List<Callable<String>> candidates = List.of(
() -> { Thread.sleep(200); return "Slow"; },
() -> { Thread.sleep(50); return "Fast"; },
() -> { Thread.sleep(100); return "Medium"; }
);
ExecutorService pool = Executors.newFixedThreadPool(3);
String winner = pool.invokeAny(candidates);
System.out.println("First: " + winner); // Fast
pool.shutdown();execute() и submit()
execute(Runnable) запускает задачу без отслеживания результата: объект Future не возвращается, а исключения теряются, если не добавить обработчик неперехваченных исключений. submit() оборачивает задачу в Future и сохраняет исключения, чтобы их можно было получить через get().
ExecutorService pool = Executors.newFixedThreadPool(2);
// execute: exception silently lost
pool.execute(() -> { throw new RuntimeException("Oops!"); });
// submit: exception captured in Future
Future<?> f = pool.submit(() -> { throw new RuntimeException("Oops!"); });
try {
f.get();
} catch (ExecutionException e) {
System.out.println("Caught: " + e.getCause().getMessage());
}
pool.shutdown();Задача с результатом и проверяемые исключения
В отличие от задачи без результата, задача с результатом может выбрасывать проверяемые исключения, поэтому она особенно удобна для операций ввода-вывода и работы с базами данных:
Callable<String> dbQuery = () -> {
// This compiles fine — checked exception is declared on Callable.call()
if (Math.random() < 0.5) throw new java.sql.SQLException("DB error");
return "row data";
};
Future<String> f = pool.submit(dbQuery);
try {
String data = f.get();
} catch (ExecutionException e) {
if (e.getCause() instanceof java.sql.SQLException) {
System.out.println("DB failed: " + e.getCause().getMessage());
}
}Параллельное выполнение независимых вычислений
Используйте несколько отправок задач с результатом, чтобы выполнять независимые вычисления параллельно:
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Long> sumFuture = pool.submit(() -> LongStream.rangeClosed(1,1_000_000).sum());
Future<Long> prodFuture = pool.submit(() -> LongStream.rangeClosed(1,20).reduce(1L, (a,b)->a*b));
long sum = sumFuture.get();
long prod = prodFuture.get();
System.out.println("Sum: " + sum + ", 20!: " + prod);
pool.shutdown();Объединение результатов задач с результатом
Отправьте множество задач и объедините результаты после завершения всех задач:
int N = 8;
ExecutorService pool = Executors.newFixedThreadPool(N);
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i < N; i++) {
final int chunk = i;
futures.add(pool.submit(() -> chunk * chunk)); // i^2
}
int total = 0;
for (Future<Integer> f : futures) total += f.get();
System.out.println("Sum of squares: " + total);
pool.shutdown();Ограничение времени ожидания задачи с результатом
Передайте ограничение времени в future.get(timeout, unit), чтобы не ждать бесконечно:
Future<String> f = pool.submit(() -> {
Thread.sleep(5000); // slow task
return "done";
});
try {
String result = f.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("Task timed out");
f.cancel(true); // interrupt the task
} catch (ExecutionException e) {
System.out.println("Task failed: " + e.getCause());
}Рекомендации
Краткие рекомендации:
- Используйте
submit(), а неexecute()— исключения сохраняются - Используйте задачу с результатом, когда нужно вернуть значение или обработать проверяемые исключения
- Всегда вызывайте
future.get()с ограничением времени - Используйте invokeAll для пакетной обработки, а invokeAny — когда нужен первый полученный ответ
- Всегда завершайте работу пула
Быстрая проверка
В чём главное преимущество задачи с результатом перед задачей без результата?
Итоги: задачи без результата и с результатом
Основные выводы:
- Задача без результата: не возвращает значения и не поддерживает проверяемые исключения; submit возвращает Future<?> со значением null
- Задача с результатом: возвращает V и может выбрасывать проверяемые исключения; submit возвращает Future<V>
- invokeAll: отправляет пакет задач и ждёт завершения всех
- invokeAny: возвращает первый успешный результат и отменяет остальные
- Всегда используйте submit() для сохранения исключений
Часто задаваемые вопросы
Урок «Отправка задач: Runnable и Callable» бесплатный?
Да — полный текст урока «Отправка задач: Runnable и Callable» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Отправка задач: Runnable и Callable»?
Отправляйте задачи Runnable и Callable и получайте результаты через Future. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Отправка задач: Runnable и Callable»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- ExecutorService и типы пулов потоков
- Отправка задач: Runnable и Callable
- Future и обработка ошибок
- ScheduledExecutorService для повторяющихся задач