在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。我们一般只能采用共享变量或共享存储区以及线程间通信的方式实现获得任务结果的目的。
不过,Java中,也提供了使用Callable和Future来实现获取任务结果的操作。Callable用来执行任务,产生结果,而Future用来获得结果。
Callable接口与Runnable接口是否相似,查看源码,可知Callable接口的定义如下:
@FunctionalInterface
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;
}
可以看到,与Runnable接口不同之处在于,call方法带有泛型返回值V。
V get() :获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
V get(Long timeout , TimeUnit unit) :获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常。
boolean isDone() :如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
boolean isCanceller() :如果任务完成前被取消,则返回true。
boolean cancel(boolean mayInterruptRunning) :如果任务还没开始,执行cancel(…)方法将返回false;如果任务已经启动,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(…)方法将返回false。mayInterruptRunning参数表示是否中断执行中的线程。
通过方法分析我们也知道实际上Future提供了3种功能:
- 能够中断执行中的任务
- 判断任务是否执行完成
- 获取任务执行完成后额结果。
我们通过简单的例子来体会使用Callable和Future来获取任务结果的用法。
Future模式非常适合在处理很耗时很长的业务逻辑时进行使用,可以有效的减小系统的响应时间,提高系统的吞吐量。
public class UseFuture implements Callable<String>{
private String para;
private int time=3000;
public UseFuture(String para){
this.para = para;
}
public UseFuture(String para,int time){
this.para = para;
this.time=time;
}
/**
* 这里是真实的业务逻辑,其执行可能很慢
*/
@Override
public String call() throws Exception {
//模拟执行耗时
Thread.sleep(time);
String result = this.para + "处理完成";
return result;
}
//主控制函数
public static void main(String[] args) throws Exception {
String queryStr1 = "query1";
String queryStr2 = "query2";
//构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类
FutureTask<String> future1 = new FutureTask<String>(new UseFuture(queryStr1,4000));
FutureTask<String> future2 = new FutureTask<String>(new UseFuture(queryStr2,8000));
//创建一个固定线程的线程池且线程数为1,
ExecutorService executor = Executors.newFixedThreadPool(1);
//这里提交任务future,则开启线程执行RealData的call()方法执行
//submit和execute的区别:1.submit可以传入实现callable接口的实例对象
2.submit可以有返回值
Future f1=executor.submit(future1);
f1.get()方法判断Future是否执行完成 如果为null 时表示执行完毕
Future f2=executor.submit(future2);
System.out.println("请求完毕");
// try {
// //这里可以做额外的数据操作,也就是主程序执行其他业务逻辑
// Thread.sleep(2000);
// } catch (Exception e) {
// e.printStackTrace();
// }
//调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待
long start=System.currentTimeMillis();
System.out.println("数据:" + future1.get());
System.out.println("数据:" + future2.get());
long end=System.currentTimeMillis()-start;
System.out.println("=================================="+(end));
executor.shutdown();
}
}