Последние несколько постов мы разбирались с потоками в 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()
, где мы можем указать время ожидания результата, это нужно для избежания блокировки текущего потока на длительное время. Также есть методы i
sDone()
и 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’ов идет по порядку, а последующие вразнобой?