Future
future的核心思想是:一个方法的计算过程可能非常耗时,一直在原地等待方法返回,显然不明智。可以把该计算过程放到线程池去执行,并通过Future去控制方法的计算过程,在计算出结果后直接获取该结果。
可以通俗的理解为"ajax版本的runnable",屁如:比如去吃早点时,点了包子和凉菜,包子需要等3分钟,凉菜只需1分钟,如果是串行的一个执行,在吃上早点的时候需要等待4分钟,但是因为你在等包子的时候,可以同时准备凉菜,所以在准备凉菜的过程中,可以同时准备包子,这样只需要等待3分钟。那Future这种模式就是后面这种执行模式。
Future常用方法介绍
Future保存异步计算的结果,可以在我们执行任务时去做其他工作,并提供了以下几个方法
* cancel(boolean mayInterruptIfRunning):试图取消执行的任务,参数为true时直接中断正在执行的任务,否则直到当前任务执行完成,成功取消后返回true,否则返回false
* isCancel():判断任务是否在正常执行完前被取消的,如果是则返回true
* isDone():判断任务是否已完成
* get():等待计算结果的返回,如果计算被取消了则抛出
* get(long timeout,TimeUtil unit):设定计算结果的返回时间,如果在规定时间内没有返回计算结果则抛出TimeOutException
Runnable与Callable不同点:
1. Runnable不返回任务执行结果,Callable可返回任务执行结果
2. Callable在任务无法计算结果时抛出异常,而Runnable不能
3. Runnable任务可直接由Thread的start方法或ExecutorService的submit方法去执行
使用Future的好处:
1. 获取任务的结果,判断任务是否完成,中断任务
1. Future的get方法很好的替代的了Thread.join或Thread,join(long millis)
2. Future的get方法可以判断程序代码(任务)的执行是否超时
Future图解
v get() 返回任务计算结果
package com.zhang.myjuc.a8.future;
import java.util.concurrent.*;
/**
* FutureDemo:Future的基本使用
* Callable<T>:返回结果并可能引发异常的任务。实现者定义了一个没有参数的方法call。
* Callable接口与Runnable类似,因为它们都是为实例可能由另一个线程执行的类设计的。但是,Runnable不返回结果,也不能抛出检查过的异常。
*
* @author zhangxiaoxiang
* @date 2020/08/26
*/
public class FutureDemo {
public static void main(String[] args) {
//最新阿里规约,这里为了简介,但是不用线程池工厂(要使用带有ThreadFactory参数的ThreadPoolExecutor构造方法哦,
// 这样你就可以方便的设置线程名字啦。)
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
Future<String> future = executor.submit(() -> "返回任务执行结果String类型:长草颜团子(lambda写法写法)");
// Future<String> future = executor.submit(new CallableTask());
try {
//返回任务计算结果哈
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
executor.shutdown();
}
}
/**
* 任务常规写法(lambda写法不利于初学时理解,这里添加一个常规写法)
*/
static class CallableTask implements Callable<String>{
@Override
public String call() throws Exception {
return "返回任务执行结果String类型:长草颜团子(非lambda写法写法)";
}
}
}
批量提交任务时,用List来批量接收结果 get(i)的使用
package com.zhang.myjuc.a8.future;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.*;
/**
* MultiFutures:批量提交任务时,用List来批量接收结果
*
* @author zhangxiaoxiang
* @date 2020/08/26
*/
public class MultiFutures {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 4,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20));
ArrayList<Future> futures=new ArrayList<>();
//20个线程执行注意上面线程池参数(最大线程池数量+队列排队容量>=20才行哈)
for (int i = 0; i < 20; i++) {
//JDK 8 lamdba写法
Future<Integer> future = executor.submit(()->{
//模拟任务耗时
Thread.sleep(3000);
return new Random().nextInt();
});
//用List来批量接收结果
futures.add(future);
}
// Thread.sleep(5000);
for (int i = 0; i < 20; i++) {
//返回列表中指定位置的元素 get(i)
Future future = futures.get(i);
try {
System.out.println(future.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdown();
}
}
Future 异常处理(如果任务完成,返回true。完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法将返回true)
package com.zhang.myjuc.a8.future;
import java.util.concurrent.*;
/**
* FutureException:Future处理异常(如果任务完成,返回true。完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法将返回true)
* get方法过程中抛出异常,for循环为了演示抛出Exception的时机:并不是说一产生异常就抛出,直到我们get执行时,才会抛出。
*
* @author zhangxiaoxiang
* @date 2020/08/26
*/
public class FutureException {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 20,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20));
Future<Integer> future = executor.submit(() -> {
throw new IllegalArgumentException("Callable抛出异常");
});
for (int i = 0; i < 5; i++) {
System.out.println(i);
try {
Thread.sleep(500);
//如果任务完成,返回true。完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法将返回true。
System.out.println("如果任务完成,返回true。完成可能是由于正常终止、异常或取消——在所有这些情况下," +
"此方法将返回true,本次结果是===>"+future.isDone());
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("InterruptedException 异常");
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println("ExecutionException 异常");
}
}
}
}
执行结果
Future 演示超时 get(2000, TimeUnit.MILLISECONDS)
package com.zhang.myjuc.a8.future;
import java.util.concurrent.*;
/**
* FutureTimeout:演示get的超时方法,需要注意超时后处理,调用future.cancel()。演示cancel传入true和false的区别,
* 代表是否中断正在执行的任务。
*
* @author zhangxiaoxiang
* @date 2020/08/26
*/
public class FutureTimeout {
private static final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
static class Ad {
String name;
public Ad(String name) {
this.name = name;
}
@Override
public String toString() {
return "Ad{" +
"name='" + name + '\'' +
'}';
}
}
static class FetchAdTask implements Callable<Ad> {
@Override
public Ad call() throws Exception {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("sleep期间被中断了");
return new Ad("被中断时候的默认广告");
}
return new Ad("旅游订票哪家强?找某程");
}
}
public void printAd() {
Future<Ad> f = exec.submit(new FetchAdTask());
Ad ad;
try {
//如果有必要,等待计算完成的时间最多为给定的时间,然后检索其结果(如果可用)
ad = f.get(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
ad = new Ad("被中断时候的默认广告");
} catch (ExecutionException e) {
ad = new Ad("异常时候的默认广告");
} catch (TimeoutException e) {
ad = new Ad("超时时候的默认广告");
System.out.println("超时,未获取到广告");
boolean cancel = f.cancel(true);
System.out.println("cancel的结果:" + cancel);
}
exec.shutdown();
System.out.println(ad);
}
public static void main(String[] args) {
FutureTimeout timeout = new FutureTimeout();
timeout.printAd();
}
}
运行结果