在之前一篇文章中介绍了 Future 设计模式的设计思想以及具体实现,今天我们来讲一下使用 JDK 原生实现。
JDK 内置的 Future 设计模式主要使用到了 Callable
接口和 FutureTask
类。
Callable
Callable
是类似于 Runnable
的接口,实现 Callable
接口的类和实现 Runnable
的类都是可被其他线程执行的任务。Callable
接口的定义如下:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Callable 的类型参数是返回值的类型。例如:
Callable<Integer>
表示一个最终返回 Integer 对象的异步计算。
FutureTask
在认识 FutureTask
之前我们先来了解一下保存计算结果的接口类 Future
在实际应用中可以启动一个计算,将 Future对象
交给某个线程,然后执行其他操作。Future
对象的所有者在结果计算好之后就可以获得它。Future
接口具有下面的方法:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled(); boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
第一个 get 方法的调用被阻塞,直到计算完成。
如果在计算完成之前,第二个 get 方法的调用超时,抛出一个 TimeoutException
异常。
如果运行该计算的线程被中断,两个方法都将抛出 InterruptedException
。
如果计算已经完成,那么 get 方法立即返回。如果计算还在进行,isDone
方法返回 false;如果完成了,则返回 true。
可以用 cancel
方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果 mayInterrupt
参数为 true,它就被中断。
认识了 Future 类后我们再来认识 FutureTask,FutureTask
包装器是一种非常便利的机制,同时实现了 Future
和 Runnable
接口。FutureTask 有 2 个构造方法:
public FutureTask(Callable<V> callable) {
if (callable == null) throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
通常,我们会使用 Callable 示例构造一个 FutureTask 对象,并将它提交给线程池进行处理,下面我们将展示这个内置的 Future 模式的使用。
public class RealData implements Callable<String> {
private String param;
public RealData(String param){
this.param = param;
}
@Override
public String call() throws Exception {
StringBuffer sb = new StringBuffer();
for(int i = 0 ; i< 10 ;i++){
sb.append(param); try {
Thread.sleep(100);
}catch (InterruptedException e){
}
}
return sb.toString();
}
}
上述代码实现了 Callable
接口,它的 Call
方法会构造我们需要的真实数据并返回,当然这个过程比较缓慢,这里使用 Thread.sleep()来模拟它:
public class FutureMain {
public static void main(String[] args)
throws ExecutionException, InterruptedException {
//构造FutureTask
FutureTask<String> futureTask = new FutureTask<String>(new RealData("xxx"));
ExecutorService executorService = Executors.newFixedThreadPool(1);
//执行FutureTask,发送请求
//在这里开启线程进行RealData的call()执行
executorService.submit(futureTask);
System.out.println("请求完毕。。。");
try {
//这里可以进行其他额外的操作,这里用sleep代替其他业务的处理
Thread.sleep(200);
}catch (InterruptedException e) {
e.printStackTrace();
}
//获取call()方法的返回值
//如果此时call()方法没有执行完成,则依然会等待
System.out.println("真实数据:"+futureTask.get());
}
}
上述代码就是使用 Future 模式的典型。构造 FutureTask
时使用实现 Callable
接口的实现类,告诉 FutureTask
我们需要的数据应该有返回值。然后将 FutureTask
提交给线程池,接下来我们不用关心数据是怎么产生的,可以去做其他的业务逻辑处理,然后在需要的时候调用 FutureTask.get()
得到实际的数据。
Future 模式在日常业务中处理复杂业务时会经常用到,希望大家都能掌握。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于