目录
3、FutureTask 到 CompletableFurue
7、CompletableFuture.anyOf 选择执行最快的
8、CompletableFuture.allOf 所有任务执行完毕
1、Future接口
Future接口定义了操作异步执行的方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务是否执行完毕等。
各个方法的含义
cancel(boolean) | 尝试取消当前任务的执行。如果任务已经取消、已经完成或者其他原因不能取消,尝试将失败。如果任务还没有启动就调用了cancel(true),任务将永远不会被执行。如果任务已经启动,参数mayInterruptIfRunning将决定任务是否应该中断执行该任务的线程,以尝试中断该任务。 如果任务不能被取消,通常是因为它已经正常完成,此时返回false,否则返回true |
get() | 等待任务结束,然后获取结果,如果任务在等待过程中被终端将抛出InterruptedException,如果任务被取消将抛出CancellationException,如果任务中执行过程中发生异常将抛出ExecutionException。 |
get(long,TimeUnit) | 任务最多在给定时间内完成并返回结果,如果没有在给定时间内完成任务将抛出TimeoutException |
isCancelled() | 如果任务在正常结束之前被被取消返回true |
isDone() | 正常结束、异常或者被取消导致任务完成,将返回true |
Future是Java5中加的一个接口,它提供了一种异步并行计算的功能。如果主线程需要执行一个耗时的计算任务,我们就可以通过Future把这个任务放到异步线程中执行。主线程继续处理其他任务或先行结束,再通过Future获取计算结果。
2、FutureTask引出
在多线程中需要有三个条件需要被满足:多线程、有返回、异步任务
线程的申明有根本就是两种方式,一种继承Thread重写run方法,另一种就是实现Runnable接口实现run方法。多线程实现的方式与基础知识
1、线程接受自定义的参数几乎都需要Runnable这个参数
2、RunnableFuture<V> 接口实现了Runable接口和Future接口,同时满足了有返回,异步任务
3、FuntureTask<V> 实现了RunnableFuture<V>接口,通过构造函数将Callable传入到FutureTask中
代码实例
public class CallableThreadTest implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + " 执行");
return Thread.currentThread().getName() + " 返回值";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableThreadTest callableThread = new CallableThreadTest();
FutureTask<String> futureTask = new FutureTask<>(callableThread);
Thread t1 = new Thread(futureTask);
t1.setName("线程1");
t1.start();
String s = futureTask.get();
System.out.println(s);
}
}
3、FutureTask 到 CompletableFurue
3.1 FutureTask 优点
FutrueTask + 线程池可以提高多任务配合,可以显著提高执行效率
串行化执行:
FutureTask + 线程池:
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
long startTime = System.currentTimeMillis();
FutureTask<String> task1 = new FutureTask<>(() -> {
Thread.sleep(500);
return "task1 返回结果";
});
executorService.submit(task1);
FutureTask<String> task2 = new FutureTask<>(() -> {
Thread.sleep(500);
return "task2 返回结果";
});
executorService.submit(task2);
FutureTask<String> task3 = new FutureTask<>(() -> {
Thread.sleep(300);
return "task3 返回结果";
});
executorService.submit(task3);
// 等待所有任务执行完毕
while (!(task1.isDone() && task2.isDone() && task3.isDone())){
}
long endTime = System.currentTimeMillis();
System.out.println(task1.get());
System.out.println(task2.get());
System.out.println(task3.get());
System.out.println("----执行了" + (endTime - startTime) + "毫秒");
System.out.println(Thread.currentThread().getName() + "---------end");
// 关闭线程池 最好放在finally中
executorService.shutdown();
}
3.2 FutureTask缺点
3.2.1 get()阻塞
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> task1 = new FutureTask<>(() -> {
Thread.sleep(5000);
return "task1 返回结果";
});
Thread thread = new Thread(task1);
thread.setName("自定义线程");
thread.start();
long startTime = System.currentTimeMillis();
// 这里会阻塞 所以一般把get()方法放到最后
System.out.println(task1.get());
long endTime = System.currentTimeMillis();
System.out.println("----get()阻塞了" + (endTime - startTime) + "毫秒");
System.out.println(Thread.currentThread().getName() + "---------end");
}
3.2.2 isDone()轮询
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> futureTask = new FutureTask<String>(()->{
System.out.println(Thread.currentThread().getName()+"\t ------副线程come in");
try {
TimeUnit.SECONDS.sleep(5);//暂停几秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task over";
});
Thread t1 = new Thread(futureTask,"t1");
t1.start();
System.out.println(Thread.currentThread().getName()+"\t-------主线程忙其他任务了");
//1------- System.out.println(futureTask.get(3,TimeUnit.SECONDS));//只愿意等3秒,过了3秒直接抛出异常
//2-------更健壮的方式-------轮询方法---等副线程拿到才去get()
//但是也会消耗cpu资源
while(true){
if(futureTask.isDone()){
System.out.println(futureTask.get());
break;
}else{
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在处理中------------正在处理中");
}
}
}
3.2.3 不可以多个线程任务前后依赖
4、CompletableFuture用法介绍
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
CompletionStage 实现多个任务前后依赖,一个任务完成后,可以继续下一个阶段。
核心四个静态方法
// 没有返回值,并且使用ForkJoinPool线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 没有返回值,使用自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
// 有返回值,并且使用ForkJoinPool线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 有返回值,使用自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
使用方法:
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 无返回参数,默认线程池
System.out.println("--无返回参数,默认线程池 开始--");
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long startTime = System.currentTimeMillis();
System.out.println(voidCompletableFuture.get() + "-返回值 执行了" + (System.currentTimeMillis() - startTime) + "毫秒");
System.out.println("--无返回参数,默认线程池 结束--");
System.out.println();
System.out.println();
// 无返回参数,使用自定义线程
System.out.println("--无返回参数,使用自定义线程 开始--");
CompletableFuture<Void> voidCompletablePoolFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},executorService);
startTime = System.currentTimeMillis();
System.out.println(voidCompletablePoolFuture.get() + "-返回值 执行了" + (System.currentTimeMillis() - startTime) + "毫秒");
System.out.println("--无返回参数,使用自定义线程 结束--\t\t\t");
System.out.println();
System.out.println();
// 有返回参数,默认线程池
System.out.println("--有返回参数,默认线程池 开始--");
CompletableFuture<String> objectCompletableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "helllo supplyasync";
});
startTime = System.currentTimeMillis();
System.out.println(objectCompletableFuture.get() + "-返回值 执行了" + (System.currentTimeMillis() - startTime) + "毫秒");
System.out.println("--有返回参数,默认线程池 结束--\t\t\t");
System.out.println();
System.out.println();
// 有返回参数,使用自定义线程
System.out.println("--有返回参数,使用自定义线程 开始--");
CompletableFuture<String> objectCompletablePoolFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "helllo supplyasync";
},executorService);
startTime = System.currentTimeMillis();
System.out.println(objectCompletablePoolFuture.get() + "-返回值 执行了" + (System.currentTimeMillis() - startTime) + "毫秒");
System.out.println("--有返回参数,使用自定义线程 结束--\t\t\t");
executorService.shutdown();
}
减少阻塞和轮询whenCompleta
CompletableFuture通过whenComplete来减少阻塞和轮询(回调)
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"--------副线程come in");
// 产生随机数
int result = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
int t = 6/0;
return result;
}).whenComplete((v,e) -> {
// 不管有没有异常,都会进到这个方法中,v是值,e是异常
System.out.println("------------------进入到whenComplete方法,v = " + v);
}).exceptionally(e->{
// 有异常的情况
e.printStackTrace();
System.out.println("异常情况"+e.getCause()+"\t"+e.getMessage());
return null;
});
//线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:暂停3秒钟线程
System.out.println(Thread.currentThread().getName()+"线程先去忙其他任务");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
CompletableFuture常用API
1、获得结果和触发计算
获取结果
// 不见不散,容易阻塞
public T get()
// 过时不候,超过时间会抛异常
public T get(long timeout,TimeUnit unit)
// 类似于get(),区别在于是否需要抛出异常
public T join()
// 立即获取结果不阻塞;
// 计算完,返回计算完成后的结果;
// 没算完,返回设定的valueAbsent(直接返回了备胎值xxx)
public T getNow(T valueIfAbsent)
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// 执行需要2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "abc";
});
long startTime = System.currentTimeMillis();
// 活立即返回结果,如果future先执行完成返回的是future返回值,否则返回的是valueIfAbsent
System.out.println("getNow返回值" + future.getNow("xxx"));
System.out.println("执行了 "+ (System.currentTimeMillis() - startTime) +" 毫秒");
}
//---------------
getNow返回值xxx
执行了 3 毫秒
//---------------
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// 执行需要2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "abc";
});
long startTime = System.currentTimeMillis();
try {
//等待需要1秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 活立即返回结果,如果future先执行完成返回的是future返回值,否则返回的是valueIfAbsent
System.out.println("getNow返回值" + future.getNow("xxx"));
System.out.println("执行了 "+ (System.currentTimeMillis() - startTime) +" 毫秒");
}
//---------------
getNow返回值abc
执行了 3005 毫秒
//---------------
主动触发计算
// 是否立即打断get()方法返回括号值
// 返回true表示打断了获取这个过程,直接返回了备胎值value
// 如果没打断,返回false 和原来方法返回
public boolean complete(T value)
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// 执行需要2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "abc";
});
long startTime = System.currentTimeMillis();
try {
// 睡眠时间为小于future时间 输出 true completeValue
// 睡眠时间为大于future时间 输出 false abc
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//执2-等1 返回true+备胎值completeValue
System.out.println(future.complete("completeValue") + "\t" + future.join());
System.out.println("执行了 "+ (System.currentTimeMillis() - startTime) +" 毫秒");
}
2、对计算结果进行处理
thenApply 计算结果存在依赖关系,使得线程串行化。因为依赖关系,所以一旦有异常,后面的thenApply不再执行。
public static void main(String[] args) throws ExecutionException, InterruptedException{
//当一个线程依赖另一个线程时用 thenApply 方法来把这两个线程串行化,
CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("111");
return 1024;
}).thenApply(f -> {
// 异常情况:那步出错就停在那步。
int age = 10/0;
System.out.println("222");
return f + 1;
}).thenApply(f -> {
// 前面的thenApply异常后就不会进入到该方法
System.out.println("333");
return f + 1;
}).whenCompleteAsync((v,e) -> {
// 该方法怎么都会进入
System.out.println("*****v: " + v);
}).exceptionally(e -> {
// 抛异常才会进入
e.printStackTrace();
return null;
});
System.out.println("-----主线程结束,END");
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
}
handle 类似于thenApply,但是又异常的话仍然可以下一步。
public static void main(String[] args) throws ExecutionException, InterruptedException{
//当一个线程依赖另一个线程时用 handle 方法来把这两个线程串行化,
// 异常情况:有异常也可以往下一步走,根据带的异常参数可以进一步处理
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("111");
return 1024;
}).handle((f, e) -> {
int age = 10 / 0;//异常语句
System.out.println("222");
return f + 1;
}).handle((f, e) -> {
System.out.println("333");
return Objects.isNull(f) ? null : (f + 1);
}).whenCompleteAsync((v, e) -> {
System.out.println("*****v: " + v);
}).exceptionally(e -> {
e.printStackTrace();
return null;
});
System.out.println("-----主线程结束,END");
System.out.println("future返回值:" + future.join());
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
}
-----主线程结束,END
111
333
*****v: null
future返回值:null
3、对计算结果进行消费
接收任务的处理结果,并消费处理,无返回结果|消费型函数接口
thenRun
thenRun(Runnable runnable)
没有入参,没有返回值;任务A执行完执行任务B,并且任务B不需要A的入参,也没有反参;
thenAccept
thenAccept(Consumer action)
有入参,没有返回值;任务A执行完执行任务B,B需要A的结果,但是任务B无返回值
thenApply
thenApply(Function fn)
有入参,有返回值;
public static void main(String[] args){
// null
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
// resultA打印出来的 null因为没有返回值
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(System.out::print).join());
//resultA resultB 返回值
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(resultA -> resultA + " resultB").join());
}
4、CompleteFuture和线程池说明
上面的几个方法都有一般和后面加Async的版本
以thenRun和thenRunAsync为例,有什么区别?
结论:
1、没有传入自定义线程池,使用默认线程池ForkJoinPool
2、调用thenRun第二个任务和前一个任务使用同一个线程池;使用thenRunAsync执行第二个任务的时候,第一个使用的是传入的线程池,第二个使用的是默认的ForkJoinPool线程池。
注意:有可能处理太快,系统优化切换原则,直接使用main线程池处理,如下:
public static void main(String[] args){
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println("1号任务"+"\t"+Thread.currentThread().getName());
return "abcd";
},threadPool).thenRun(()->{
System.out.println("2号任务"+"\t"+Thread.currentThread().getName());
}).thenRun(()->{
System.out.println("3号任务"+"\t"+Thread.currentThread().getName());
}).thenRun(()->{
System.out.println("4号任务"+"\t"+Thread.currentThread().getName());
});
threadPool.shutdown();
}
1号任务 pool-1-thread-1
2号任务 main
3号任务 main
4号任务 main
由以下代码可以看出,thenRun会沿用之前任务使用的线程池,而thenRunAsync是使用默认的线程池
public static void main(String[] args){
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(()->{
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("1号任务"+"\t"+Thread.currentThread().getName());
return "abcd";
},threadPool).thenRun(()->{
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("2号任务"+"\t"+Thread.currentThread().getName());
}).thenRunAsync(()->{
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("3号任务"+"\t"+Thread.currentThread().getName());
}).thenRun(()->{
try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("4号任务"+"\t"+Thread.currentThread().getName());
});
try {TimeUnit.MILLISECONDS.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
threadPool.shutdown();
}
1号任务 pool-1-thread-1
2号任务 pool-1-thread-1
3号任务 ForkJoinPool.commonPool-worker-1
4号任务 ForkJoinPool.commonPool-worker-1
5、对计算速度进行选用
applyToEither方法,那个快使用那个线程的结果
public static void main(String[] args) throws ExecutionException, InterruptedException{
CompletableFuture<String> play1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
return "play1 ";
});
CompletableFuture<String> play2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return "play2";
});
//对计算速度进行选用
CompletableFuture<String> thenCombineResult = play1.applyToEither(play2, f -> f + " is winner");
System.out.println(Thread.currentThread().getName() + "\t" + thenCombineResult.get());
}
ForkJoinPool.commonPool-worker-1 ---come in
ForkJoinPool.commonPool-worker-2 ---come in
main play2 is winner
6、对计算结果进行合并
thenCombine 合并:将两个运算结果交给thenCombine来处理
public static void main(String[] args) throws ExecutionException, InterruptedException{
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
return 10;
});
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
return 20;
});
CompletableFuture<Integer> thenCombineResult = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
return x + y;
});
System.out.println(thenCombineResult.get());
}
ForkJoinPool.commonPool-worker-1 ---come in
ForkJoinPool.commonPool-worker-1 ---come in
main ---come in
30
7、CompletableFuture.anyOf 选择执行最快的
public static void main(String[] args){
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
return "A";
});
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return "B";
});
CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return "C";
});
CompletableFuture<String> futureD = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return "D";
});
long startTime = System.currentTimeMillis();
// 选择执行最快的
CompletableFuture<Object> future = CompletableFuture.anyOf(futureA, futureB, futureC, futureD);
System.out.println(future.join());
long endTime = System.currentTimeMillis();
System.out.println("----执行了" + (endTime - startTime) + "毫秒");
}
B
----执行了1013毫秒
8、CompletableFuture.allOf 所有任务执行完毕
public static void main(String[] args){
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
return "A";
});
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return "B";
});
CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return "C";
});
CompletableFuture<String> futureD = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return "D";
});
long startTime = System.currentTimeMillis();
// 所有任务执行完毕
CompletableFuture.allOf(futureA, futureB, futureC, futureD).join();
long endTime = System.currentTimeMillis();
System.out.println("----执行了" + (endTime - startTime) + "毫秒");
}
----执行了3004毫秒