Последние несколько постов мы разбирались с потоками в Java. Сегодня мы продолжим тему многопоточности в Java и поговорим о Java Callable: разберем теорию и посмотрим пример использования.
Очень часто при работе с потоками нам нужно получать какой-то результат и было бы очень удобно, чтобы поток сам возвращал результаты своей работы. Именно поэтому еще в Java 5 был введен интерфейс java.util.concurrent.Callable. Он очень похож на интерфейс Runnable, но может вернуть результат в виде объекта Object и способен бросать исключения.
Интерфейс Callable использует дженерики для определения типа возвращаемого объекта. Класс Executors предоставляет полезные методы для выполнения Callable в пуле потоков. Callable таски (задачи) возвращают java.util.concurrent.Future объект. Используя Future мы можем узнать статус Callable таска и получить возвращенный объект. Это обеспечивает get() метод, который ждет завершение Callable, чтобы вернуть результат.
Future обеспечивает метод cancel() для отмены Callable таска. Есть перегруженный вариант метода get(), где мы можем указать время ожидания результата, это нужно для избежания блокировки текущего потока на длительное время. Также есть методы isDone() и isCancelled(), которые используются для получения статуса ассоциированного Callable таска.
Вот простой пример использования Callable таска, который возвращает имя потока. В примере используется Executor framework для одновременного выполнения 100 задач и Future, чтобы получить результат выполнения:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package ua.com.prologistic; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000); // возвращает имя потока, который выполняет callable таск return Thread.currentThread().getName(); } public static void main(String args[]){ //Получаем ExecutorService утилитного класса Executors с размером gпула потоков равному 10 ExecutorService executor = Executors.newFixedThreadPool(10); //создаем список с Future, которые ассоциированы с Callable List<Future<String>> list = new ArrayList<Future<String>>(); // создаем экземпляр MyCallable Callable<String> callable = new MyCallable(); for(int i=0; i< 100; i++){ //сабмитим Callable таски, которые будут //выполнены пулом потоков Future<String> future = executor.submit(callable); //добавляя Future в список, //мы сможем получить результат выполнения list.add(future); } for(Future<String> fut : list){ try { // печатаем в консоль возвращенное значение Future // будет задержка в 1 секунду, потому что Future.get() // ждет пока таск закончит выполнение System.out.println(new Date()+ "::" + fut.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } executor.shutdown(); } } |
Результат выполнения программы (часть вывода):
|
1 2 3 4 5 6 7 8 9 |
Tue Nov 10 20:48:22 PST 2015::pool-1-thread-1 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-2 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-3 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-4 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-5 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-6 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-7 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-8 Tue Nov 10 20:48:23 PST 2015::pool-1-thread-9 |
В следующих статьях мы разберем Java FutureTask и ScheduledThreadPoolExecutor. Следите за обновлениями.




Отличный подробный простой прозрачный пример. Спасибо большое, пойду сделаю себе чашечку такого же. 🙂
Почему цикл рассчитан на 100 итераций, а в list добавляется только 10 future’ов? Я так понимаю, что ответ будет: «потому что executor рассчитан на 10 нитей». Но тогда опять возвращаюсь к вопросу о необходимости цикла на 100 итераций.. Спасибо!
Почему первый вывод 10 future’ов идет по порядку, а последующие вразнобой?