先简单举个生活中的例子,比如说我们现在到中午了,想吃饭,这时候我们有两个选择,一个是去饭馆打包,另一种是拿起手机叫外卖。
第一种呢需要我们自己亲自去饭馆,而且需要等待饭馆做好饭后我们再吃,这个过程可以看成是需要等待的,在程序上是同步的。
第二种我们拿手机定好饭馆饭菜后,由饭馆的人做饭并叫外卖小哥送餐到宿舍,那么在这个时间段内就可以去做其他事情,不必一直等待,。在程序上可以看成是异步的。
而java提供的future模式就是给我们写代码逻辑时的异步实现,在这里我们会写个代码例子来具体说明,下图中的客户端Future=我们自己,FutureData包装=饭馆,RealData真实数据类=饭菜,请看:
我们现在模拟在程序中遇到耗时比较久的操作使用异步,同时之后处理其他事项,在处理完其他事项后,再去获取之前异步的请求结果。具体的代码如下:
主函数类:
package com.lydon.thread.future;
public class Main {
public static void main(String[] args) {
FutureClient futureClient=new FutureClient();
System.out.println("开始异步请求。。。");
FutureData futureData=futureClient.request();
try {
System.out.println("模拟主线程处理其他事情开始。。。");
Thread.sleep(2000);
System.out.println("模拟主线程处理其他事情完成。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
String result=futureData.getRequest();
System.out.println("获取异步请求结果为"+result);
}
}
FutureClient:
package com.lydon.thread.future;
public class FutureClient {
public FutureData request(){
FutureData futureData = new FutureData();
new Thread(()->{
RealData realData=new RealData();
futureData.setData(realData);
}).start();
return futureData;
}
}
FutureData
package com.lydon.thread.future;
public class FutureData implements Data {
private RealData realData;
@Override
public String getRequest() {
synchronized (this){
while (realData==null){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.getRequest();
}
}
public void setData(RealData realData){
synchronized (this){
this.realData=realData;
notifyAll();
}
}
}
Data
package com.lydon.thread.future;
public interface Data {
String getRequest();
}
RealData
package com.lydon.thread.future;
public class RealData implements Data{
private String result="";
RealData(){
System.out.println("模拟远端处理数据===开始===");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("模拟远端处理数据===结束===");
result="查询结果aaa";
}
@Override
public String getRequest() {
return result;
}
}
输出结果为:
以上是自己根据future实现的简单示例,通过上面的实例大家应该也了解得差不多,接下来就要讲future自身的使用了
java在JDK 5中引入了Future模式。Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算。
Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了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;
}
Future接口的方法介绍如下:
- boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
- boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
- boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
- V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
- V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计 算超时,将抛出TimeoutException
一般情况下,我们会结合Callable和Future一起使用,通过ExecutorService的submit方法执行Callable,并返回Future。
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(() -> { //Lambda 是一个 callable, 提交后便立即执行,这里返回的是 FutureTask 实例
System.out.println("running task");
Thread.sleep(10000);
return "return task";
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("do something else"); //前面的的 Callable 在其他线程中运行着,可以做一些其他的事情
try {
System.out.println(future.get()); //等待 future 的执行结果,执行完毕之后打印出来
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} finally {
executor.shutdown();
}
比起future.get(),y一般更推荐使用get (long timeout, TimeUnit unit) 方法,设置了超时时间可以防止程序无限制的等待future的结果。虽然future提供了方便的异步实现,但是大家应该也会发现,在实际场景中,可能需要对这种异步操作的结果进行监听,这样才知道什么时候需要在得到异步操作结果再去做其他事项,可能会包括多项异步任务之间的依赖,而这些对于原始的future都是不满足的,我们要么使用阻塞,在拿结果的地方等待future返回的结果,这时又变成同步操作。要么使用轮询地判断Future是否完成,这样会耗费CPU的资源。
为了解决以上问题,Java 8新增的CompletableFuture类在吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,让Java拥有了完整的非阻塞编程模型:Future、Promise 和 Callback(在Java8之前,只有无Callback 的Future)。
CompletableFuture能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。
CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。
具体的CompletableFuture介绍在其他章节会专门讲解,敬请期待