Ответ
Основные интерфейсы — Runnable и Callable<V>.
1. Интерфейс Runnable
- Определяет задачу без возвращаемого значения и без возможности проброса проверяемых исключений.
- Используется с
ThreadилиExecutorService.
Runnable task = () -> {
System.out.println("Задача выполняется в потоке: " + Thread.currentThread().getName());
};
// Запуск через Thread
new Thread(task).start();
// Или через ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(task);
2. Интерфейс Callable<V>
- Определяет задачу, которая возвращает результат типа
Vи может бросать проверяемые исключения. - Используется только с
ExecutorService, который возвращает объектFuture<V>для получения результата.
Callable<Integer> computation = () -> {
TimeUnit.SECONDS.sleep(1);
return 42;
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> futureResult = executor.submit(computation);
// Блокирующее получение результата
Integer result = futureResult.get();
System.out.println("Результат: " + result); // Результат: 42
Ключевое отличие: Callable предпочтительнее, когда задача должна вернуть результат или требует обработки проверяемых исключений в рамках механизма Future.
Ответ 18+ 🔞
Так, слушай, про потоки. Есть две главные штуки, как задачу в отдельную нитку сунуть — Runnable и Callable<V>. Разница, блядь, принципиальная, сейчас разжуёшь.
1. Runnable — старый добрый дед
Этот интерфейс, сука, как тот немой Герасим — работает молча, нихуя не возвращает и проверяемые исключения наружу выплюнуть не может, только проглотить. Кинул задачу — и забыл. Используется либо с голым Thread, либо с ExecutorService.
Runnable task = () -> {
System.out.println("Задача выполняется в потоке: " + Thread.currentThread().getName());
};
// Запуск через Thread
new Thread(task).start();
// Или через ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(task);
Вот и всё, ебушки-воробушки. Запустил и пошёл дальше. Результата не жди — его просто нет.
2. Callable<V> — интеллигент с возвратом
А вот это уже интереснее. Этот чувак может вернуть результат (любого типа V) и, внимание, имеет право заявить: «Ёпта, у меня тут InterruptedException вылез!». Работает ТОЛЬКО с ExecutorService, который возвращает тебе Future<V> — такую расписку, что результат будет, но позже.
Callable<Integer> computation = () -> {
TimeUnit.SECONDS.sleep(1); // Может спать, может исключение кидать — ему можно!
return 42; // А главное — вернёт ответ!
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> futureResult = executor.submit(computation);
// Тут поток заблокируется, пока результат не приплывёт
Integer result = futureResult.get();
System.out.println("Результат: " + result); // Результат: 42
Короче, суть в чём: если тебе нужно просто что-то сделать в фоне и плевать на ответ — бери Runnable. Если же задача должна что-то вернуть или у неё есть проверяемые исключения, которые ты хочешь через Future обработать — твой выбор Callable. Всё просто, как три копейки, блядь.