FutureTask 异步回调介绍
FutureTask 方式包含了一系列的 Java 相关的类,在 java.util.concurrent 包中。其中最为重要的是 FutureTask 类和 Callable 接口。
Callable 接口介绍
在介绍 Callable 接口之前,先得重提一下旧的 Runnable 接口。Runnable 接口是在 Java 多线程中表示线程的业务代码的抽象接口。但是,Runnable 有一个重要的问题,它的 run 方法是没有返回值的。正因为如此,Runnable 不能用于需要有返回值的应用场景。为了解决 Runnable 接口的问题,Java 定义了一个新的和 Runnable 类似的接口—— Callable接口。并且将其中的代表业务处理的方法命名为 call,call 方法有返回值。
Callable的代码:
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
//call 方法有返回值
V call() throws Exception;
}
Callable 接口可以对应到 Runnable 接口;Callable 接口的 call 方法可以对应到 Runnable接口的 run 方法。相比较而言,Callable 接口的功能更强大一些。
Callable 接口与 Runnable 接口相比,还有一个很大的不同:Callable 接口的实例不能作为 Thread线程实例的 target 来使用;而 Runnable 接口实例可以作为 Thread 线程实例的 target 构造参数,开启一个 Thread 线程。
问题来了,Java 中的线程类型,只有一个 Thread 类,没有其他的类型。如果 Callable 实例需要异步执行,就要想办法赋值给 Thread 的 target 成员,一个 Runnable 类型的成员。为此,Java 提供了在 Callable 实例和 Thread 的 target 成员之间一个搭桥的类——FutureTask 类。
FutureTask 类介绍
FutureTask 类代表一个未来执行的任务,表示新线程所执行的操作。
FutureTask 类的构造函数的源代码:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask 类就像一座搭在 Callable 实例与 Thread 线程实例之间的桥。FutureTask 类的内部封装一个 Callable 实例,然后自身又作为 Thread 线程的 target。
总体来说,FutureTask 类首先是一个搭桥类的角色,FutureTask 类能当作 Thread 线程去执行目标 target,被异步执行;其次,如果要获取异步执行的结果,需要通过 FutureTask 类的方法去获取,在 FutureTask 类的内部,会将 Callable 的 call 方法的真正结果保存起来,以供外部获取。
在 Java 语言中,将 FutureTask 类的一系列操作,抽象出来作为一个重要的接口——Future 接口。当然,FutureTask 类也实现了此接口。
Future 接口介绍
Future 接口不复杂,主要是对并发任务的执行及获取其结果的一些操作。
主要提供了 3 大功能:
(1)判断并发任务是否执行完成。
(2)获取并发的任务完成后的结果。
(3)取消并发执行中的任务。
Future 接口的代码:
package java.util.concurrent;
public interface Future<V> {
//取消并发任务的执行。
boolean cancel(booleanmayInterruptRunning);
//获取并发任务的取消状态。
booleanisCancelled();
//获取并发任务的执行状态。
booleanisDone();
//获取并发任务执行的结果。(阻塞的)
V get() throws InterruptedException,ExecutionException;
//获取并发任务执行的结果。(超过设定的时间限制,就会抛出异常)
V get(long timeout,TimeUnitunit) throws InterruptedException,
ExecutionException,TimeoutException;
}
使用 FutureTask 类实现异步泡茶喝的实践案例
流程图:
代码实现:
public class JavaFutureDemo {
public static final int SLEEP_GAP = 500;
public static String getCurThreadName() {
return Thread.currentThread().getName();
}
static class HotWater implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
//睡一段时间 代表烧水
Thread.sleep(SLEEP_GAP);
System.out.println("水烧好了。");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("烧水 线程发生异常中断");
return false;
}
System.out.println("烧水 线程完成");
return true;
}
}
static class Wash implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
try {
System.out.println("洗茶杯");
System.out.println("洗水壶");
//睡一段时间 洗水壶
Thread.sleep(SLEEP_GAP);
System.out.println("洗完了。");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("清洗 线程完成");
return false;
}
System.out.println("清洗 线程结束");
return true;
}
}
public static void main(String[] args) {
Callable<Boolean> hotWater = new HotWater();
FutureTask<Boolean> hFuture = new FutureTask<>(hotWater);
Thread hThread = new Thread(hFuture, "烧水线程~~~");
Callable<Boolean> wash = new Wash();
FutureTask<Boolean> wFuture = new FutureTask<>(wash);
Thread wThread = new Thread(wFuture, "清洗线程~~~");
hThread.start();
wThread.start();
Thread.currentThread().setName("主线程。");
try {
Boolean hBoolean = hFuture.get();
Boolean wBoolean = wFuture.get();
if (hBoolean && wBoolean) System.out.println("可以泡茶了,泡茶喝");
else if (!hBoolean) System.out.println("水没烧好,烧水线程出现问题");
else if (!wBoolean) System.out.println("还没有清洗好,清洗线程出现问题");
else System.out.println("两个线程都有问题");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(getCurThreadName() + "运行结束!");
}
}
首先,在上面的喝茶实例代码中使用了 Callable 接口,替代了 Runnable 接口,并且在 call 方法中返回了异步线程的执行结果。
其次,从 Callable 异步逻辑到异步线程,需要创建一个 FutureTask 类的实例,并通过 FutureTask类的实例,创建新的线程
最后,通过 FutureTask 类的实例,取得异步线程的执行结果。