Future接口
Future接口(FutureTask实现类)定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。
比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务后, 主线程就去做其他事情了,忙其它事情或者先执行完,过了一会才去获取子任务的执行结果或变更的任务状态。
主线程 不中断的情况下,新建一个异步线程去完成某个任务(耗时)
Future接口常用的实现类FutureTask
三个特点:多线程/有返回/异步任务
实现了RunableFuture 接口
再通过构造注入 注入Callable
public class FutureTaskTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask=new FutureTask<>(new MyThread2());
Thread t1=new Thread(futureTask,"t1");
t1.start();
System.out.println(futureTask.get());
}
}
class MyThread2 implements Callable<String>{
@Override
public String call() throws Exception {
return "HELLO";
}
}
Future编码实战和优缺点分析
future+线程池异步多线程任务配置,能显著提高程序的执行效率
public class FutureThreadPoolDemo {
public static void main(String[] args) {
//3个任务,目前开启多个线程来处理 耗时多少
ExecutorService executorService= Executors.newFixedThreadPool(3);
long startTime=System.currentTimeMillis();
FutureTask<Integer> futureTask1=new FutureTask<>(()->{
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
});
FutureTask<Integer> futureTask2=new FutureTask<>(()->{
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
});
executorService.submit(futureTask1);
executorService.submit(futureTask2);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long endTime=System.currentTimeMillis();
System.out.println(endTime-startTime);
executorService.shutdown();
}
public static void m1() {
//3个任务,目前一个线程main来处理 耗时多少
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
缺点:
get()阻塞
@Test
public void test1() throws ExecutionException, InterruptedException {
FutureTask<String> futureTask=new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName()+"-----come in");
TimeUnit.SECONDS.sleep(3);
return "task over";
});
Thread t1=new Thread(futureTask,"t1");
t1.start();
System.out.println(futureTask.get());//阻塞状态
System.out.println(Thread.currentThread().getName()+"执行自己的任务了");
}
输出结果 t1-----come in task over main执行自己的任务了
调用get()方法会阻塞主线程 一般建议放在程序最后面
加入我不愿意等待很长事件,我希望过时不候,可以自动离开 futureTask.get(3,TimeUnit.SECONDS)设置最长等待时间
isDone()轮询
@Test
public void test2() throws ExecutionException, InterruptedException {
FutureTask<String> futureTask=new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName()+"-----come in");
TimeUnit.SECONDS.sleep(4);
System.out.println(Thread.currentThread().getName()+"-----执行完了");
return "task over";
});
Thread t1=new Thread(futureTask,"t1");
t1.start();
System.out.println(Thread.currentThread().getName()+"执行自己的任务了");
while (true){
if(futureTask.isDone()){
System.out.println(futureTask.get());
break;
}else {
System.out.println("正在处理中");
}
}
}
轮询的方式会耗费无谓的CPU资源,而且也不见得能及时地得到计算结果 如果想要异步获取结果,通常都会以轮询的方式去获取结果 尽量不要阻塞
结论
Future对于结果的获取不太友好,要么阻塞,要么就会消耗过多资源 如果多个任务前后要互相依赖,Future就完成不了了 由此引入了CompletableFuture
CompletableFuture
CompletableFuture提供了一种观察者模式的类似机制,可以让任务执行完后通知监听的一方
·CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段 ·一个阶段的计算执行可以是一个Function,Consumer或者Runnable。.比如:Sage.thenApply(x->square(x).thenAccept (x -System.out.print(x)).thenRun(()->System.out.println()) ·一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发
CompletableFuture 对象获取方法
- CompletableFuture.runAsync( Runnable runable) 无返回值
- CompletableFuture.supplyAsync(Supplier supplier ) 有返回值
@Test
public void test1() throws ExecutionException, InterruptedException {
//未指定线程池,用默认线程池
CompletableFuture<Void> completableFuture=CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println(completableFuture.get());
}
@Test
public void test2() throws ExecutionException, InterruptedException {
ExecutorService threadPool= Executors.newFixedThreadPool(3);
//指定线程池
CompletableFuture<Void> completableFuture=CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},threadPool);
System.out.println(completableFuture.get());
threadPool.shutdown();
}
@Test
public void test3() throws ExecutionException, InterruptedException {
ExecutorService threadPool= Executors.newFixedThreadPool(3);
//指定线程池
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "ok";
},threadPool);
System.out.println(completableFuture.get());
threadPool.shutdown();
}
Code -通用演示,减少阻塞和轮询
@Test
public void test5() throws ExecutionException, InterruptedException {
ExecutorService threadPool= Executors.newFixedThreadPool(3);
//指定线程池
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"------come in");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("三秒后----出结果");
return "ok";
},threadPool).whenComplete((v,e)->{
if(e==null){
System.out.println("------成功计算完成----"+v);
}
}).exceptionally(e->{
e.printStackTrace();
System.out.println("异常情况"+e.getCause()+e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName()+"先去忙其他任务");
//监听任务是个守护线程,让主程序保持运行,才会让其执行完
TimeUnit.SECONDS.sleep(4);
threadPool.shutdown();
}
@Test
public void test5() throws ExecutionException, InterruptedException {
ExecutorService threadPool= Executors.newFixedThreadPool(3);
//指定线程池
try{
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"------come in");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("三秒后----出结果");
if(true){
throw new RuntimeException("cuowu");
}
return "ok";
},threadPool).whenComplete((v,e)->{
if(e==null){
System.out.println("------成功计算完成----"+v);
}
}).exceptionally(e->{
e.printStackTrace();
System.out.println("异常情况"+e.getCause()+e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName()+"先去忙其他任务");
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
CompletableFuture的优点
- 异步任务结束后,会自动调用某个对象的方法
- 主线程任务设置好之后,不用关系异步任务的执行
- 异步任务执行错误之后,也能抛出异常执行相应的方法
案例实战
函数式接口
Runnable
Function
Consumer/BiConsumer
Supplier
Jion 和 Get方法区别
_System.out.println(completableFuture.get()); _System.out.println(completableFuture.join());
get()在编译时候要抛出异常,join()不用
单线程案例
public class CompletableFutureMall {
static List<NetMall> list= Arrays.asList(
new NetMall("JD"),
new NetMall("TB"),
new NetMall("PDD")
);
public static List<String> getPrice( List<NetMall> list,String productName){
return list.stream().map((netMall)->{
try {
return String.format(productName+"in %s price is %.2f",netMall.getNetMallName(),netMall.calcPrice(productName));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
}
public static void main(String[] args) {
long startTime=System.currentTimeMillis();
List<String> mysql = getPrice(list, "mysql");
for (String s : mysql) {
System.out.println(s);
}
long endTime=System.currentTimeMillis();
System.out.println("查询花费时间:"+(endTime-startTime));
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class NetMall{
private String netMallName;
public double calcPrice(String productName) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
//模拟书的价格
return ThreadLocalRandom.current().nextDouble()*2+productName.charAt(0);
}
}
异步任务解决(优化查询案例)
public class CompletableFutureMall1 {
static List<NetMall1> list= Arrays.asList(
new NetMall1("JD"),
new NetMall1("TB"),
new NetMall1("PDD")
);
public static List<String> getPrice(List<NetMall1> list, String productName){
return list.stream().map((netMall)-> CompletableFuture.supplyAsync(()->{
try {
return String.format(productName+"in %s price is %.2f",netMall.getNetMallName(),netMall.calcPrice(productName));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
})).collect(Collectors.toList()).stream().map(s->s.join()).collect(Collectors.toList());
}
public static void main(String[] args) {
long startTime=System.currentTimeMillis();
List<String> mysql = getPrice(list, "mysql");
for (String s : mysql) {
System.out.println(s);
}
long endTime=System.currentTimeMillis();
System.out.println("查询花费时间:"+(endTime-startTime));
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class NetMall1 {
private String netMallName;
public double calcPrice(String productName) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
//模拟书的价格
return ThreadLocalRandom.current().nextDouble()*2+productName.charAt(0);
}
}
CompletableFuture 常用方法
获得结果和触发计算
_// System.out.println(completableFuture.get()); // System.out.println(completableFuture.get(2L,TimeUnit.SECONDS)); // System.out.println(completableFuture.join()); //如果取的时候,已经计算完成,就正常返回,未计算完成,获得一个备胎xxxx _System.out.println(completableFuture.getNow("xxxx"));
对计算结果进行处理
thenApply 计算结果存在依赖关系,这两个线程串行化 由于存在依赖关系,前一步失败 会影响 后面 程序的运行
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "ok";
}).thenApply(f->{
System.out.println(f+"22222");
return "2";
}).thenApply(e->{
System.out.println(e+"333333");
return "3";
});
System.out.println(completableFuture.join());
}
handle 计算结果存在依赖关系,这两个线程串行化 前一步失败 会影响 后面 程序的运行
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "ok";
}).handle((f,e)->{
if(e==null){
System.out.println(f+"22222");
return "2";
}
return null;
}).whenComplete((v,e)->{
System.out.println(v+"333333");
});
System.out.println(completableFuture.join());
}
对计算结果进行消费
thenAccept 接受任务的处理结果,并且消费结果,无返回值
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture.supplyAsync(()->{
return "ok";
}).thenApply(f->{
return f+2;
}).thenAccept(r ->{
System.out.println(r);
});
}
thenRun: 任务A执行完执行B,并且B不需要A的结果 thenApply:任务A执行完执行B,B需要A的结果,同时任务B也有返回值 thenAccept:任务A执行完执行B,B需要A的结果,但是任务B没有有返回值
System.out.println(CompletableFuture.supplyAsync(() -> "resultA")
.thenRun(() -> System.out.println("111")).join());
System.out.println(CompletableFuture.supplyAsync(() -> "resultA")
.thenAccept(r -> System.out.println(r)).join());
System.out.println(CompletableFuture.supplyAsync(() -> "resultA")
.thenApply(r -> r+"resultB").join());
111
null
resultA
null
resultAresultB
对计算速度进行选用
public class CompletableFutureFastDemo {
public static void main(String[] args) {
CompletableFuture<String> playA=CompletableFuture.supplyAsync(()->{
System.out.println("A come in");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "playA";
});
CompletableFuture<String> playB=CompletableFuture.supplyAsync(()->{
System.out.println("B come in");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "playB";
});
CompletableFuture<String> result=playA.applyToEither(playB,f->{
return f;
});
System.out.println(result.join());
}
}
//输出
A come in
B come in
playA
对计算结果进行合并
public class CompletableFutureCombineDemo {
public static void main(String[] args) {
CompletableFuture<Integer> integerCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "---启动");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
});
CompletableFuture<Integer> integerCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "---启动");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 2;
});
CompletableFuture<Integer> res = integerCompletableFuture1.thenCombine(integerCompletableFuture2, (x, y) -> {
System.out.println("合并两个结果");
return x + y;
});
System.out.println(res.join());
}
}
CompletableFuture 默认线程池
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("1号任务" + Thread.currentThread().getName());
return "abcd";
}).thenRun(() -> {
System.out.println("2号任务" + Thread.currentThread().getName());
}).thenRun(() -> {
System.out.println("3号任务" + Thread.currentThread().getName());
});
System.out.println(completableFuture.get());
}
1号任务ForkJoinPool.commonPool-worker-1
2号任务ForkJoinPool.commonPool-worker-1
3号任务ForkJoinPool.commonPool-worker-1
null
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("1号任务" + Thread.currentThread().getName());
return "abcd";
},executorService).thenRun(() -> {
System.out.println("2号任务" + Thread.currentThread().getName());
}).thenRun(() -> {
System.out.println("3号任务" + Thread.currentThread().getName());
});
System.out.println(completableFuture.get());
}
1号任务pool-1-thread-1
2号任务pool-1-thread-1
3号任务pool-1-thread-1
null
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("1号任务" + Thread.currentThread().getName());
return "abcd";
},executorService).thenRunAsync(() -> {
System.out.println("2号任务" + Thread.currentThread().getName());
}).thenRunAsync(() -> {
System.out.println("3号任务" + Thread.currentThread().getName());
});
System.out.println(completableFuture.get());
}
1号任务pool-1-thread-1
2号任务ForkJoinPool.commonPool-worker-1
3号任务ForkJoinPool.commonPool-worker-1
null
结论: 1没有传入自定义线程池,都用默认线程池ForkJoinPool:: 2传入了一个自定义线程池, 如果你执行第一个任务的时候,传入了一个自定义线程池: 调用thenRun方法构行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。 调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池 3备注 有可能处理太快,系统优化切换原则,直接使用main线程处理 其它如:thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是同理