觉得对你有益的小伙伴记得点个赞+关注
后续完整内容持续更新中
希望一起交流的欢迎发邮件至javalyhn@163.com
1. Future和Callable接口
1.1 Future
Future接口定义了操作异步任务执行一些方法
,如获取异步任务的执行结果
、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。
1.2 Callable
Callable接口中定义了需要有返回的任务需要实现的方法。
2. FutureTask
2.1 什么是FutureTask
FutureTask就是一个接收Callable或者Runnable+Object(返回值)参数的能够获取返回值等一系列操作的的异步线程类。
2.2 FutureTask.get()阻塞
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException
{
FutureTask<String> futureTask = new FutureTask<>(() -> {
System.out.println("-----come in FutureTask");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return ""+ThreadLocalRandom.current().nextInt(100);
});
Thread t1 = new Thread(futureTask,"t1");
t1.start();
//A 3秒钟后才出来结果,还没有计算你提前来拿(只要一调用get方法,对于结果就是不见不散,会导致阻塞)
//System.out.println(Thread.currentThread().getName()+"\t"+futureTask.get());
//B 3秒钟后才出来结果,我只想等待1秒钟,过时不候
System.out.println(Thread.currentThread().getName()+"\t"+futureTask.get(1L,TimeUnit.SECONDS));
System.out.println(Thread.currentThread().getName()+"\t"+" run... here");
}
get方法不设置timeout
get方法设置timeout
由此可见,如果get方法没有设置到期事件,那么就会一直阻塞下去知道获取结果。如果设置了过期时间,达到事件业务仍未结束就会报异常。
2.3 FutureTask.isDone()轮询
如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要阻塞
public static void main(String[] args) throws ExecutionException, InterruptedException
{
FutureTask<String> futureTask = new FutureTask<>(() -> {
System.out.println("-----come in FutureTask");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return ""+ThreadLocalRandom.current().nextInt(100);
});
new Thread(futureTask,"t1").start();
System.out.println(Thread.currentThread().getName()+"\t"+"线程完成任务");
/**
* 用于阻塞式获取结果,如果想要异步获取结果,通常都会以轮询的方式去获取结果
*/
while(true)
{
if (futureTask.isDone())
{
System.out.println(futureTask.get());
break;
}
}
}
由此可见,轮询的方式会耗费无谓的CPU资源,而且也不见得能及时地得到计算结果.
3. Future的功能增强版CompletableFuture
3.1 CompletableFuture类
3.2 CompletionStage接口
代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符传参数。
3.3 CompletableFuture类与CompletionStage接口架构说明
3.4 CompletableFuture四个核心静态方法
-
runAsync -> 无返回值
-
public static CompletableFuture<Void> runAsync(Runnable runnable)
-
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
-
-
spplyAsync -> 有返回值
-
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
-
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
-
上述Executor executor表示线程池,如果没有指定Executor的方法,默认使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码
。
有关线程池详解请参照 juejin.cn/post/719397…
3.5 无返回值代码演示
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName()+"\t"+"-----come in");
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("-----task is over");
});
System.out.println(future.get());
}
3.6 有返回值代码演示
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "-----come in");
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return ThreadLocalRandom.current().nextInt(100);
});
System.out.println(completableFuture.get());
}
3.7 减少阻塞和轮询代码演示
从Java8开始引入了CompletableFuture,它是Future的功能增强版
,
可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法
public static void main(String[] args) throws Exception
{
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "-----come in");
int result = ThreadLocalRandom.current().nextInt(10);
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("-----计算结束耗时1秒钟,result: "+result);
if(result > 6)
{
int age = 10/0;
}
return result;
}).whenComplete((v,e) ->{
if(e == null)
{
System.out.println("-----result: "+v);
}
}).exceptionally(e -> {
System.out.println("-----exception: "+e.getCause()+"\t"+e.getMessage());
return -44;
});
//主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:暂停3秒钟线程
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
}
result <= 6
result > 6
为什么主线程不要立刻结束?因为默认线程池中的线程都是守护线程
3.8 CompletableFuture优点
- 异步任务结束时,会自动回调某个对象的方法;
- 异步任务出错时,会自动回调某个对象的方法;
- 主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行;
4. CompletableFuture常用方法
4.1 获得结果和触发计算
-
获取结果
- public T get() 阻塞直到有结果
- public T get(long timeout, TimeUnit unit) 带有过期时间,超时则报错
- public T getNow(T valueIfAbsent) 没有计算完成的情况下,给我一个替代结果,
立即获取结果不阻塞
- 计算完,返回计算完成的结果
- 没有计算完成,返回给定的valueAbsent值
- public T join()
下面是getNow方法的代码演示
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return 533;
});
//去掉注释上面计算没有完成,返回444
//开启注释上满计算完成,返回计算结果
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(completableFuture.getNow(444));
}
-
主动触发计算
- public boolean complete(T value) 是否打断get方法立即返回括号值
下面是代码演示
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return 533;
});
//注释掉暂停线程,get还没有算完只能返回complete方法设置的444;暂停2秒钟线程,异步线程能够计算完成返回get
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
//当调用CompletableFuture.get()被阻塞的时候,complete方法就是结束阻塞并get()获取设置的complete里面的值.
System.out.println(completableFuture.complete(444)+"\t"+completableFuture.get());
}
不开启开启睡眠注释
开启睡眠注释
4.2 对计算结果进行处理
- 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 -> {
System.out.println("222");
return f + 1;
}).thenApply(f -> {
//int age = 10/0; // 异常情况:那步出错就停在那步。
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 有异常也可以往下一步走,根据带的异常参数可以进一步处理
public static void main(String[] args) throws ExecutionException, InterruptedException
{
//当一个线程依赖另一个线程时用 handle 方法来把这两个线程串行化,
// 异常情况:有异常也可以往下一步走,根据带的异常参数可以进一步处理
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 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(); }
}
4.3 对计算结果进行消费
- thenAccept 接收任务的处理结果,并消费处理,无返回结果
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture.supplyAsync(() -> {
return 1;
}).thenApply(f -> {
return f + 2;
}).thenApply(f -> {
return f + 3;
}).thenApply(f -> {
return f + 4;
}).thenAccept(r -> System.out.println(r));
}
补充:任务间的顺序执行
- thenRun(Runnable runnable) 任务 A 执行完执行 B,并且 B 不需要 A 的结果
- thenAccept(Consumer action) 任务 A 执行完执行 B,B 需要 A 的结果,但是任务 B
无返回值
- thenApply(Function fn) 任务 A 执行完执行 B,B 需要 A 的结果,同时任务 B
有返回值
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(resultA -> {}).join());
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(resultA -> resultA + " resultB").join());
4.4 对计算速度进行选用
- applyToEither 谁快用谁
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
return 10;
});
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return 20;
});
CompletableFuture<Integer> thenCombineResult = completableFuture1.applyToEither(completableFuture2,f -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in ");
return f + 1;
});
System.out.println(Thread.currentThread().getName() + "\t" + thenCombineResult.get());
}
4.5 对计算结果进行合并
- thenCombine 两个CompletionStage任务都完成后,最终能把两个任务的结果一起交给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());
}
public static void main(String[] args) throws ExecutionException, InterruptedException
{
CompletableFuture<Integer> thenCombineResult = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in 1");
return 10;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in 2");
return 20;
}), (x,y) -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in 3");
System.out.println("x====" + x + "y====" + y);
return x + y;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in 4");
return 30;
}),(a,b) -> {
System.out.println(Thread.currentThread().getName() + "\t" + "---come in 5");
System.out.println("x====" + a + "y====" + b);
return a + b;
});
System.out.println("-----主线程结束,END");
System.out.println(thenCombineResult.get());
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
}
补充
- thenAcceptBoth 计算结果合并(无返回值)
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
return 10;
}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
return 22;
}),(x,y) -> {
System.out.println("x===" + x + "\t" + "y===" + y);
});
}
- thenCompose 将前一个任务的返回结果作为下一个任务的参数
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
return 10;
}).thenCompose((x) -> CompletableFuture.supplyAsync(() -> {
System.out.println(x);
return "CompletableFuture";
}));
}