В прошлом уроке мы разобрали что такое Java Collable Future
и как его эффективно использовать в своих программах. Сегодня у нас на очереди Java FutureTask
: небольшая теория и пример использования.
FutureTask
базируется на конкретной реализации Future
интерфейса и обеспечивает асинхронную обработку. Он содержит методы для запуска и отмены таска, а также методы получения состояния FutureTask. Нам нужен callable объект для создания future таска, а затем мы можем использовать Java Thread Pool Executor для асинхронной обработки.
Давайте посмотрим FutureTask
в работе на примере простой программы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package ua.com.prologistic; import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { private long waitTime; public MyCallable(int timeInMillis){ this.waitTime = timeInMillis; } @Override public String call() throws Exception { Thread.sleep(waitTime); // возвращает имя потока, который выполняет этот callable таск return Thread.currentThread().getName(); } } |
На примере ниже представлены часто используемые методы FutureTask
в действии:
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 52 53 54 55 |
package ua.com.prologistic; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class FutureTaskExample { public static void main(String[] args) { MyCallable callable1 = new MyCallable(1000); MyCallable callable2 = new MyCallable(2000); // создаем 2 future таска для 2х callable объектов FutureTask<String> futureTask1 = new FutureTask<String>(callable1); FutureTask<String> futureTask2 = new FutureTask<String>(callable2); // екзекьютор с размером пула в 2 потока ExecutorService executor = Executors.newFixedThreadPool(2); // стартуем executor.execute(futureTask1); executor.execute(futureTask2); // выполняем в бесконечном цикле, пока // executor service не закончит выполнение всех future тасков while (true) { try { if(futureTask1.isDone() && futureTask2.isDone()){ System.out.println("Done"); // заканчиваем работу executor service executor.shutdown(); return; } if(!futureTask1.isDone()){ // ждем, пока future task не закончит выполнение System.out.println("Результат выполнения FutureTask1 = " + futureTask1.get()); } System.out.println("Ждем, пока FutureTask2 не закончит свое выполнение"); String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); if(s !=null){ System.out.println("Результат выполнения FutureTask2 = " + s); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }catch(TimeoutException e){ //оставим пустым } } } } |
Когда мы запустим представленную выше программу, то она ничего не будет выводить в консоль в течение некоторого времени. Все дело в том, что метод get()
класса FutureTask
ждет полного выполнения задачи и лишь затем возвращает объект с результатом выполнения.
Также существует перегруженный метод, который ждет только определенное время — мы использовали его для futureTask2.
Также обратите внимание на использование метода isDone()
, который используется для того, чтобы убедиться в успешном выполнении всех задач и что программа завершена.
Результат выполнения программы:
1 2 3 4 5 6 7 8 |
Результат выполнения FutureTask1 = pool-1-thread-1 Ждем, пока FutureTask2 не закончит свое выполнение Ждем, пока FutureTask2 не закончит свое выполнение Ждем, пока FutureTask2 не закончит свое выполнение Ждем, пока FutureTask2 не закончит свое выполнение Ждем, пока FutureTask2 не закончит свое выполнение Результат выполнения FutureTask2 = pool-1-thread-2 Программа закончила работу |
Обязательно ознакомьтесь с Java Callable и следите за обновлениями раздела Многопоточность и параллелизм в Java.
collable мля xD