Некоторое время назад я написал пост об интерфейсах Java Callable Future, которые мы можем использовать для получения преимуществ параллельной обработки потоков, а также того, что они способны возвращать значение вызывающей программе.
FutureTask является базовой конкретной реализацией интерфейса Future и обеспечивает асинхронную обработку. Он содержит методы для запуска и отмены задачи, а также методы, которые могут возвращать состояние FutureTask как завершенное или отмененное. Нам нужен вызываемый объект для создания будущей задачи, а затем мы можем использовать Java Thread Pool Executor для их асинхронной обработки.
Давайте посмотрим на пример FutureTask с простой программой.
Поскольку FutureTask требует вызываемого объекта, мы создадим простую реализацию Callable.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package com.journaldev.threads; 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); //return the thread name executing this callable task return Thread.currentThread().getName(); } } |
Вот пример метода FutureTask, и он показывает часто используемые методы FutureTask.
01
02
03
04
05
06
07
08
09
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 com.journaldev.threads; 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 ); FutureTask<String> futureTask1 = new FutureTask<String>(callable1); FutureTask<String> futureTask2 = new FutureTask<String>(callable2); ExecutorService executor = Executors.newFixedThreadPool( 2 ); executor.execute(futureTask1); executor.execute(futureTask2); while ( true ) { try { if (futureTask1.isDone() && futureTask2.isDone()){ System.out.println( "Done" ); //shut down executor service executor.shutdown(); return ; } if (!futureTask1.isDone()){ //wait indefinitely for future task to complete System.out.println( "FutureTask1 output=" +futureTask1.get()); } System.out.println( "Waiting for FutureTask2 to complete" ); String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); if (s != null ){ System.out.println( "FutureTask2 output=" +s); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e){ //do nothing } } } } |
Когда мы запустим вышеуказанную программу, вы заметите, что она какое-то время ничего не печатает, потому что метод get()
из FutureTask ожидает завершения задачи, а затем возвращает выходной объект. Существует также перегруженный метод, который позволяет ожидать только указанное количество времени, и мы используем его для futureTask2. Также обратите внимание на использование метода isDone()
чтобы убедиться, что программа завершается после выполнения всех задач.
Вывод вышеуказанной программы будет:
1
2
3
4
5
6
7
8
|
FutureTask1 output=pool-1-thread-1 Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete Waiting for FutureTask2 to complete FutureTask2 output=pool-1-thread-2 Done |
Таким образом, FutureTask не дает никаких преимуществ, но он удобен, когда мы хотим переопределить некоторые методы интерфейса Future и не хотим реализовывать каждый метод интерфейса Future.