简介
回顾创建线程的几种方式
- 通过继承Thread类或实现Runnable接口,重写
run
方法来创建一个线程,通过调用线程实例的start
方法启动线程执行; - 实现Callable接口,比起第一种方式,这种方式的好处就是配合FutureTask对象,可以得到线程执行完毕的返回结果。
public class ThreadTest { public static void main(String[] args) throws ExecutionException, InterruptedException { MyTread myTread = new MyTread(); FutureTask<Integer> futureTask = new FutureTask<>(myTread); futureTask.run(); // 阻塞式方法 会等待线程执行完毕获取返回结果 Integer i = futureTask.get(); System.out.println(i); // 20 } public static class MyTread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("线程开始执行..."); int i = 100 / 5; System.out.println("线程执行完毕..."); return i; } } }
CompletableFuture的作用
上述一些原生实现线程的方式存在很多的局限性,比如不能对一个任务(方法)内的多个操作进行异步、组合等操作。CompletableFuture实现了Future接口,它可以对我们方法内的操作进行异步编排的能力,并且可以指定各个异步操作的执行顺序,也就是线程串行化。
举一个形象的例子:
在一个方法中实现了A、B、C三个功能,假设每个功能的执行所需要的时间分别为(5s,10s,15s),在没有异步编排之前,进入方法依次执行 A功能—>B功能—>C功能,执行方法完毕,用时30s。当我们在方法中引入异步编排,给A、B、C每个功能都分配一个不同的线程执行,这样的情况下,最快15s这个方法就可以执行完毕,这就是所谓的异步。
另一个应用场景:
B功能必须要依赖A功能执行完毕,我们也可以使用CompletableFuture来指定只有A功能执行完毕才能执行B功能,这就是所谓的线程串行化。
CompletableFuture用法
注意:以下所有带Async的方法都表示执行下一步操作会去线程池中寻找空闲的线程执行,也就是异步执行(如果使用相同的线程池,也可能是相同的线程执行)。不带Async执行任务的线程和执行完该任务去执行下一步相应的操作使用的线程是相同的;
创建线程
CompletableFuture创建线程的四种方式:
- 前两种可以获取线程执行完毕的返回值,后两者不可以;
- 带有Executor参数的可以传入自定义的线程池,不传则使用默认的。
runAsync(Runnable runnable, Executor executor)
public class ThreadTest02 { // 自定义线程池 public static final ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) { CompletableFuture.runAsync(()->{ System.out.println("线程开始执行..."); int i = 100/5; System.out.println("运行结果:" + i); //运行结果:20 }, executorService); } }
supplyAsync(Supplier<U> supplier, Executor executor)
public class ThreadTest02 { // 自定义线程池 public static final ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("线程开始执行..."); int i = 100 / 5; System.out.println("运行结果:" + i); //运行结果:20 return i; }, executorService); // 线程执行完毕的返回值 Integer i = future.get(); System.out.println("获取到返回值:" + i); // 获取到返回值:20 } }
线程任务完成时回调方法
方法:
whenComplete
可以处理正常和异常的计算结果;exceptionally
处理异常情况;whenComplete
:执行当前任务的线程继续来执行whenComplete
的任务;
-whenCompleteAsync
:将whenCompleteAsync
需要执行的任务交给线程池来进行执行;- 不带Async执行任务的线程和执行完任务去执行回调方法whenComplete使用的线程是相同的;带Async执行回调方法会去线程池中寻找空闲的线程执行(如果使用相同的线程池,也可能是相同的线程执行)。
whenCompleteAsync:
public class ThreadTest02 { // 自定义线程池 public static final ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("线程开始执行..."); int i = 100 / 5; System.out.println("运行结果:" + i); return i; }, executorService).whenCompleteAsync((result, exception) -> { System.out.println("线程执行完毕, 返回结果:" + result + "异常:" + exception); // 线程执行完毕, 返回结果:20异常:null }); } }
exceptionally:
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("线程开始执行..."); int i = 100 / 0; System.out.println("运行结果:" + i); return i; }, executorService).whenCompleteAsync((result, exception) -> { System.out.println("线程执行完毕, 返回结果:" + result + "异常:" + exception); //线程执行完毕, 返回结果:null异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero }).exceptionally(throwable -> { return 33; }); Integer i = future.get(); System.out.println("获取到返回值:" + i); // 获取到返回值:33 }
当执行的任务中存在异常,使用exceptionally可以改变返回结果(whenComplete可以处理正常和异常的计算结果,但不能修改返回果)。
handle
和exceptionally一样,可以对任务执行完毕进行后续处理,不同的是该方法在任务正常或异常都可以对其进行后续处理。
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("线程开始执行...");
int i = 100 / 20;
System.out.println("运行结果:" + i);
return i;
}, executorService).whenCompleteAsync((result, exception) -> {
System.out.println("线程执行完毕, 返回结果:" + result + "异常:" + exception);
// 线程执行完毕, 返回结果:5异常:null
}).handle((result, throwable) -> {
Integer rs = null;
if (result != null) {
rs = result * 2;
} else if (throwable != null) {
rs = 0;
}
return rs;
});
Integer i = future.get();
System.out.println("获取到返回值:" + i); // 获取到返回值:10
}
线程串行化
比如A,B两个任务,B任务必须要等待A任务执行完毕后才能执行,我们就要使这两个任务串行化执行。
方法:
thenRun
:只要上一个任务执行完毕,就开始执行thenRun任务;thenAccept
:上一个任务执行完毕后,接收它的返回值,再开始执行thenAccept任务;thenApply
:上一个任务执行完毕后,接收它的返回值,执行thenApplyAsync任务,并返回当前任务的返回值。
thenRun:
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> { System.out.println("任务A开始执行..."); int i = 100 / 20; System.out.println("任务A执行完毕, 运行结果:" + i); return i; }, executorService).thenRun(() -> { System.out.println("任务B开始执行..."); }); }
output:
任务A开始执行…
任务A执行完毕, 运行结果:5
任务B开始执行…thenAccept:
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> { System.out.println("任务A开始执行..."); int i = 100 / 20; System.out.println("任务A执行完毕, 运行结果:" + i); return i; }, executorService).thenAccept((result) -> { System.out.println("任务B开始执行, 接受到任务A的返回结果:" + result); }); }
output:
任务A开始执行…
任务A执行完毕, 运行结果:5
任务B开始执行, 接受到任务A的返回结果:5thenApply:
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("任务A开始执行..."); int i = 100 / 20; System.out.println("任务A执行完毕, 运行结果:" + i); return i; }, executorService).thenApply((result) -> { System.out.println("任务B开始执行, 接受到任务A的返回结果:" + result); return 20; }); Integer i = future.get(); System.out.println("获取到任务B的返回值:" + i); }
output:
任务A开始执行…
任务A执行完毕, 运行结果:5
任务B开始执行, 接受到任务A的返回结果:5
获取到任务B的返回值:20
两任务组合–都要完成
两个任务必须都要完成,触发该任务。
方法:
thenCombine:
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
System.out.println("任务A开始执行...");
System.out.println("任务A执行完毕...");
return "A";
}, executorService);
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
System.out.println("任务B开始执行...");
System.out.println("任务B执行完毕...");
return "B";
}, executorService);
CompletableFuture<String> futureC = futureA.thenCombine(futureB, (firstResult, secondResult) -> {
System.out.println("任务C开始执行...");
System.out.println("接收到任务A的返回值:" + firstResult);
System.out.println("接收到任务B的返回值:" + secondResult);
return "C";
});
String i = futureC.get();
System.out.println("获取到任务C的返回值:" + i);
}
output:
任务A开始执行…
任务A执行完毕…
任务B开始执行…
任务B执行完毕…
任务C开始执行…
接收到任务A的返回值:A
接收到任务B的返回值:B
获取到任务C的返回值:C
两任务组合–完成一个
两个任务中,其中一个完成,触发该任务执行。
方法:
applyToEither:
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
System.out.println("任务A开始执行...");
System.out.println("任务A执行完毕...");
return "A";
}, executorService);
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
System.out.println("任务B开始执行...");
System.out.println("任务B执行完毕...");
return "B";
}, executorService);
CompletableFuture<String> futureC = futureA.applyToEither(futureB, (result) -> {
System.out.println("任务C开始执行...");
System.out.println("接收到执行完毕的任务的返回值:" + result);
return "C";
});
String i = futureC.get();
System.out.println("获取到任务C的返回值:" + i);
}
多任务组合
方法:
allOf
:必须等待所有的任务都执行完毕,才能继续执行,否则阻塞;anyOf
:只要其中一个任务执行完毕,则继续执行。
-
allOf
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> { System.out.println("任务A开始执行..."); System.out.println("任务A执行完毕..."); return "A"; }, executorService); CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> { System.out.println("任务B开始执行..."); System.out.println("任务B执行完毕..."); return "B"; }, executorService); CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> { System.out.println("任务C开始执行..."); System.out.println("任务C执行完毕..."); return "C"; }, executorService); // 直到所有任务都执行完毕 才能继续往下执行 CompletableFuture<Void> allOf = CompletableFuture.allOf(futureA, futureB, futureC); allOf.get(); System.out.println("A:" + futureA.get() + "B:" + futureB.get() + "C:" + futureC.get()); }
output:
任务A开始执行…
任务A执行完毕…
任务B开始执行…
任务B执行完毕…
任务C开始执行…
任务C执行完毕…
A:AB:BC:C -
anyOf
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> { System.out.println("任务C开始执行..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("任务C执行完毕..."); return "C"; }, executorService); // 有一个任务执行完毕 则继续往下执行 CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureA, futureB, futureC); anyOf.get(); System.out.println("执行完毕任务的返回值:" + anyOf.get()); }
output:
任务A开始执行…
任务A执行完毕…
任务B开始执行…
任务B执行完毕…
执行完毕任务的返回值:A
任务C开始执行…
任务C执行完毕…