文章目录
- 概述
- Thread 和 Runnable
- Thread
- Runnable
- Thread 和 Runnable 的关系
- 小问题
- Callable、Future 和 FutureTask
- Callable
- FutureTask
- Future接口
- Callable 和 FutureTask 的关系
- Thread 、Runnable、FutureTask 和 Callable 的关系
- CompletableFuture
- 常用方法概述
- get() 和 join() 方法区别
- supplyAsync
- runAsync
- thenApply
- thenApplyAsync(自定义线程)
- thenAccept
- thenAcceptAsync(自定义线程)
- thenRun
- thenRunAsync(自定义线程)
- thenCombine
- thenAcceptBoth
- runAfterBoth
- allOf
- anyOf
概述
平时多线程开发大多是使用Runnable,Callable,Thread,FutureTask,ThreadPoolExecutor等方式开启多线程,这些方式开启多线程的优势是成本不高。但是通过上述几种方式开启多线程很多时候没有办法很方便的去完成异步编程的操作,原因如下:
- Thread + Runnable:可以执行异步任务,没有返回结果,无法抛出异常
- Callable + Future + FutureTask:可以执行异步任务,可以获取返回结果,可以抛出异常,所以当你需要返回多线程的数据,就需要借助
Callable 和 Future- 获取返回结果,如果基于get方法获取,线程需要挂起在WaitNode里
- 获取返回结果,也可以基于isDone判断任务的状态,但是这里需要不断轮询
综上:上述的方式都是有一定的局限性的。
CompletableFuture就是帮你处理这些任务之间的逻辑关系,编排好任务的执行方式后,任务会按照规划好的方式一步一步执行,不需要让业务线程去频繁的等待。
比如说任务A,任务B,还有任务C。其中任务B还有任务C执行的前提是任务A先完成,再执行任务B和任务C。
CompletionStage接口定义了任务编排的方法,执行某一阶段,可以向下执行后续阶段。异步执行的,默认线程池是ForkJoinPool.commonPool(),但为了业务之间互不影响,且便于定位问题,强烈推荐使用自定义线程池。
Thread 和 Runnable
Thread
部分源代码如下:
public class Thread implements Runnable { private Runnable target;@Overridepublic void run() {if (target != null) {target.run();}}//.........
}
简单使用方式:继承该类并重写其run()方法
public class MyThread01 extends Thread {public MyThread01(String name) {super(name);}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "已经运行");}public static void main(String[] args) {new MyThread01("线程一").start();}
}
Runnable
部分源代码如下:该接口只有一个方法,所以它是一个函数式接口
@FunctionalInterface
public interface Runnable {public abstract void run();
}
简单使用方式:实现该接口并重写其run()方法
public class MyThread02 implements Runnable {@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "已经运行");}public static void main(String[] args) {new Thread(new MyThread02(),"线程二").start();}
}
Thread 和 Runnable 的关系
Thread类本身也实现了Runable接口,并重写了Runbale接口提供的run方法。
Thread方式是继承Thread类,Runbale方式是实现Runable接口。
虽然上面说的是两种方式启动多线程,但是本质都是使用了JDK提供的Thread类来启动多线程的。
注意:Runbale方式的实现是因为Thread类提供了入参为Runable类型的构造方法。
小问题
为什么线程启动调用的是Thread.start()方法,而不是Thread.run()方法?
Callable、Future 和 FutureTask
为什么已经有了 Thread 和 Runnable 这两种创建线程的方式,还要出现另外的那?
其实对于 Thread 和 Runable,其 run() 都是无返回值的,并且无法抛出异常,所以当你需要返回多线程的数据,就需要借助 Callable 和 Future。
Callable
部分源码如下:该接口只有一个方法,所以它也是一个函数式接口
@FunctionalInterface
public interface Callable<V> {V call() throws Exception;
}
Callable接口在:java.util.concurrent包下功能:类似于Runnable接口,可以创建一个可以被其他线程执行的实例差别:Callable接口- 可以有返回值- 可以抛出异常- 方法不同,一个是run(),一个是call()
注意:java启动线程必须要和Thread类扯上关系。
FutureTask
部分源码如下:
public class FutureTask<V> implements RunnableFuture<V> {public void run() {// ......}// ......
}
java启动线程必须要和Thread类扯上关系,通过查Thread类发现,此类的构造方法中只能接收Runnable的实现类,所以要想使用Callable创建线程,必须要和Thread或者Runnable接口扯上关系。
经过查看JDK文档发现:
- Runnable有个实现类FutureTask,在java.util.concurrent包下面
- FutureTask有个构造可以接收Callable接口的实现类
- 所以,FutureTask 也是个 Runnable,所以当执行
Thread.run()
时,其实是执行的FutureTask.run()
。
于是乎我们可以这样
- 1.创建一个类实现Callable接口(通过泛型指定返值的类型)
- 2.把实现类的实例放到FutureTask的构造中,FutureTask是Runnable接口的实现类
- 3.把FutureTask的实例放到Thread中用来创建线程
简单使用方式:
public class CallableTest01 {public static void main(String[] args) {MyThread03 myThread03 = new MyThread03();// 2.把实现类的实例放到FutureTask的构造中,FutureTask是Runnable接口的实现类FutureTask futureTask=new FutureTask(myThread03);// 3.把FutureTask的实例放到Thread中用来创建线程new Thread(futureTask,"Thread_A").start();// 使用匿名内部类的方式new Thread(new FutureTask<String>(new MyThread03()),"Thread_B").start();}
}// 1.创建一个类实现Callable接口(通过泛型指定返值的类型)
//注意:泛型的参数等于方法的返回值
class MyThread03 implements Callable<String>{@Overridepublic String call() throws InterruptedException{System.out.println("Callable.call.....");return "aismall";}
}
Future接口
部分源码如下:
public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
前面说了借助 Callable 和 Future可以获取多线程的返回值,这个返回值在FutureTask里面,使用get方法获取返回值,FutureTask.get()方法是由Future接口提供的。
简单使用方式:
public class CallableTest01 {public static void main(String[] args) {MyThread03 myThread03 = new MyThread03();// 2.把实现类的实例放到FutureTask的构造中,FutureTask是Runnable接口的实现类FutureTask futureTask=new FutureTask(myThread03);// 3.把FutureTask的实例放到Thread中用来创建线程new Thread(futureTask,"Thread_A").start();// 使用匿名内部类的方式new Thread(new FutureTask<String>(new MyThread03()),"Thread_B").start();try {String returnValue=(String) futureTask.get(); //此方法会产生阻塞,一般往后放System.out.println(returnValue);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}// 1.创建一个类实现Callable接口(通过泛型指定返值的类型)
//注意:泛型的参数等于方法的返回值
class MyThread03 implements Callable<String>{@Overridepublic String call() throws InterruptedException{System.out.println("Callable.call.....");return "aismall";}
}
对于 FutureTask,Callable 就是他的任务,而 FutureTask 内部维护了一个任务状态,所有的状态都是围绕这个任务来进行的,随着任务的进行,状态也在不断的更新。
FutureTask 继承了 Future,实现对任务的取消、数据获取、任务状态判断等功能。
比如我们经常会调用 get() 方法获取数据,如果任务没有执行完成,会将当前线程放入阻塞队列等待,当任务执行完后,会唤醒阻塞队列中的线程。
Callable 和 FutureTask 的关系
FutureTask 初始化时,Callable 必须作为 FutureTask 的初始化入参:
public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW; // ensure visibility of callable}
当执行 FutureTask.run() 时,其实执行的是 Callable.call():
public void run() {if (state != NEW ||!RUNNER.compareAndSet(this, null, Thread.currentThread()))return;try {// CallableCallable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {// Callable.call()result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)set(result);}} finally {runner = null;int s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}
}
Thread 、Runnable、FutureTask 和 Callable 的关系
现在我们应该可以很清楚知道 :Thread 、Runnable、FutureTask 和 Callable 的关系
- Thread.run() 执行的是 Runnable.run():因为Thread类实现了Runable接口的run()方法,所以执行顺序是:Thread.run()——》Runable.run()。
- FutureTask.run() 执行的是 Callable.run():因为内部做了处理,最终执行的是Callable接口实现类的run方法,所以执行顺序是:FutureTask.run()—》Callable.run()。
- FutureTask 继承了 Runnable接口并实现了该接口的run方法,所以执行顺序是:Thread.run()——》Runnable.run()—》FutureTask.run()—》Callable.run()。
最终线程的启动都是使用Thread.start()方法来完成的。
CompletableFuture
常用方法概述
-
supplyAsync() :开启异步(创建线程执行任务),有返回值
-
thenApply() :拼接异步任务,前置任务(必须有返回值)处理完之后,将返回结果交给后置任务,然后后置任务再执行,后置任务也有返回结果。
-
thenAccept():拼接异步任务,前置任务(必须有返回值)处理完之后,将返回结果交给后置任务,然后后置任务再执行,后置任务没有返回结果。
-
runAsync() :开启异步(创建线程执行任务),无返回值。
-
thenRun():拼接异步任务,前置任务(返回值可有可无)处理完之后,不接受前置任务执行结果,然后后置任务再执行,后置任务也没有返回结果。
-
get():阻塞线程,主要可以: ①获取线程中的异常然后处理异常、②设置等待时间
-
join():阻塞线程,推荐使用 join() 方法,因为它没有受到 interrupt 的干扰,不需要捕获异常,也不需要强制类型转换。他自己会抛出异常。
-
thenCombine():处理并行任务,如有任务A,任务B,任务C,任务A和任务B并行执行,等到任务A和任务B全部执行完毕后,再执行任务C,当前方式前置任务需要有返回结果,后置任务接收前置任务的结果,有返回值。
-
thenAcceptBoth():处理并行任务,如有任务A,任务B,任务C,任务A和任务B并行执行,等到任务A和任务B全部执行完毕后,再执行任务C,当前方式前置任务需要有返回结果,后置任务接收前置任务的结果,没有返回值。
-
runAfterBoth():处理并行任务,如有任务A,任务B,任务C。任务A和任务B并行执行,等到任务A和任务B全部执行完毕后,再执行任务C,当前方式前置任务不需要有返回结果,后置任务不会接收前置任务的结果,没有返回值。
-
applyToEither():如有任务A,任务B,任务C。任务A和任务B并行执行,只要任务A
或者
任务B执行完毕,开始执行任务C,可以接收结果并且返回结果。 -
acceptEither():如有任务A,任务B,任务C。任务A和任务B并行执行,只要任务A
或者
任务B执行完毕,开始执行任务C,可以接收结果没有返回结果。 -
runAfterEither():如有任务A,任务B,任务C。任务A和任务B并行执行,只要任务A
或者
任务B执行完毕,开始执行任务C,不接收结果也没返回结果。 -
exceptionally():拼接异步任务,但是只有前面业务执行时出现异常了,才会执行当前方法来处理,:返回默认值或者一个替代的 CompletableFuture 对象,从而避免系统的崩溃或异常处理的问题。
-
handle():类似exceptionally()。
-
allOf():让内部编写多个CompletableFuture的任务,多个任务都执行完后,才会继续执行你后续拼接的任务,返回的CompletableFuture是Void,没有返回结果。
-
anyOf():让内部编写多个CompletableFuture的任务,只要有一个任务执行完毕就继续执行后续,最先执行完的任务做作为返回结果的入参。
get() 和 join() 方法区别
get() 和 join() 方法:都可以阻塞线程,等所有任务都执行完了再执行后续代码。
CompletableFuture 中的 get() 和 join() 方法都用于获取异步任务的执行结果,但是在使用时需要注意以下几点区别:
-
抛出异常的方式不同:如果异步任务执行过程中出现异常, get() 方法会抛出 ExecutionException 异常,而 join() 方法会抛出 CompletionException 异常,这两个异常都是继承自 RuntimeException 的。
-
方法调用限制不同: join() 方法是不可以被中断的,一旦调用就必须等待任务执行完成才能返回结果;而 get() 方法可以在调用时设置等待的超时时间,如果超时还没有获取到结果,就会抛出 TimeoutException 异常。
-
返回结果类型不同: get() 方法返回的是异步任务的执行结果,该结果是泛型类型 T 的,需要强制转换才能获取真正的结果;而 join() 方法返回的是异步任务的执行结果,该结果是泛型类型 T,不需要强制转换。
-
推荐使用方式不同:推荐在 CompletableFuture 中使用 join() 方法,因为它没有受到 interrupt 的干扰,不需要捕获异常,也不需要强制类型转换。
综上所述, get() 方法和 join() 方法都是获取异步任务的执行结果,但是在使用时需要根据具体场景选择使用哪个方法。如果需要获取执行结果并且不希望被中断,推荐使用 join() 方法;如果需要控制等待时间或者需要捕获异常,则可以使用 get() 方法。
supplyAsync
示例代码:
public static void main(String[] args) {// 开启异步(创建线程执行任务),有返回值CompletableFuture<String> firstTask = CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程名字: " + Thread.currentThread().getName());return "返回结果";});System.out.println("执行吗 222222");// 阻塞等待等待线程执行完成返回结果 不用捕获异常String result1 = firstTask.join();System.out.println("result1: " + result1);try {// 阻塞等待线程执行完成返回结果 需要捕获异常String result2 = firstTask.get();System.out.println("result2: " + result2);} catch (Exception e) {e.printStackTrace();}}
运行结果:
执行吗 222222
执行了吗 111111
线程名字: ForkJoinPool.commonPool-worker-1
result1: 返回结果
result2: 返回结果
小结:CompletableFuture如果不提供线程池的话,默认使用的ForkJoinPool,而ForkJoinPool内部是守护线程,如果main线程结束了,守护线程会跟着一起结束。
runAsync
示例代码:
public static void main(String[] args) {CompletableFuture<Void> firstTask = CompletableFuture.runAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程名字: " + Thread.currentThread().getName());});System.out.println("执行吗 222222");}
运行结果:
执行吗 222222
执行了吗 111111
线程名字: ForkJoinPool.commonPool-worker-1
小结:和普通的开启异步线程一样,无返回值,异步线程和main线程交替执行,谁先抢占到CPU谁先执行。
thenApply
示例代码:
public static void main(String[] args) {// 开启异步(创建线程执行任务),有返回值CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";});String resultA = taskA.join();System.out.println("resultA: " + resultA);// 拼接异步任务CompletableFuture<String> taskB = taskA.thenApply(result -> {System.out.println("任务B获取到任务A结果: " + result);System.out.println("线程B名字: " + Thread.currentThread().getName());result = result + "--222222";return result;});String resultB = taskB.join();System.out.println("resultB: " + resultB);}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
resultA: 返回结果 1111111
任务B获取到任务A结果: 返回结果 1111111
线程B名字: main
resultB: 返回结果 1111111--222222
小结:有任务A还有任务B,任务B需要在任务A执行完毕后再执行,而且任务B需要任务A的返回结果,任务B自身也有返回结果,thenApply可以拼接异步任务,前置任务处理完之后,将返回结果交给后置任务,然后后置任务再执行
thenApplyAsync(自定义线程)
示例代码:
public static void main(String[] args) {// 自定义线程池ExecutorService executor = Executors.newFixedThreadPool(10);// 开启异步(创建线程执行任务),有返回值CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";}).thenApplyAsync(result -> { // 拼接异步任务,指定自定义线程池System.out.println("任务B获取到任务A结果: " + result);System.out.println("线程B名字: " + Thread.currentThread().getName());result = result + "--222222";return result;}, executor);String resultB = taskB.join();System.out.println("resultB: " + resultB);}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
任务B获取到任务A结果: 返回结果 1111111
线程B名字: pool-1-thread-1
resultB: 返回结果 1111111--222222
小结:thenApply提供了带有Async的方法,可以指定每个任务使用的具体线程池。
thenAccept
示例代码:
public static void main(String[] args) throws IOException {CompletableFuture<Void> taskB = CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";}).thenAccept(result -> {System.out.println("任务B获取到任务A结果: " + result);System.out.println("线程B名字: " + Thread.currentThread().getName());result = result + "--222222";System.out.println("result: " + result);});}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
任务B获取到任务A结果: 返回结果 1111111
线程B名字: main
result: 返回结果 1111111--222222
小结:thenAccept和thenApply使用方式一样,都是任务A和任务B的拼接,前置任务需要有返回结果,后置任务会接收前置任务的结果,不过后置任务没有返回值
。
thenAcceptAsync(自定义线程)
示例代码:
public static void main(String[] args) throws IOException {// 自定义线程池ExecutorService executor = Executors.newFixedThreadPool(10);// 开启异步(创建线程执行任务),有返回值CompletableFuture<Void> taskB = CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";}).thenAcceptAsync(result -> { // 拼接异步任务,指定自定义线程池System.out.println("任务B获取到任务A结果: " + result);System.out.println("线程B名字: " + Thread.currentThread().getName());result = result + "--222222";System.out.println("result: " + result);}, executor);}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
任务B获取到任务A结果: 返回结果 1111111
线程B名字: pool-1-thread-1
result: 返回结果 1111111--222222
小结:thenAcceptAsync和thenApplyAsync使用方式一样,都是任务A和任务B的拼接,前置任务需要有返回结果,后置任务会接收前置任务的结果,不过后置任务没有返回值
。
thenRun
示例代码:
public static void main(String[] args) throws IOException {// 开启异步(创建线程执行任务),没有返回值CompletableFuture<Void> taskB = CompletableFuture.runAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());}).thenRun(() -> { // 拼接异步任务,不获取前面任务的返回值System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());});}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 222222
线程B名字: main
小结:用法和thenApply,thenAccept一样,都是任务A和任务B的拼接,前置任务没有返回结果,后置任务不接收前置任务结果,后置任务也没有返回结果。
thenRunAsync(自定义线程)
示例代码:
public static void main(String[] args) throws IOException {// 自定义线程池ExecutorService executor = Executors.newFixedThreadPool(10);// 开启异步(创建线程执行任务),没有返回值CompletableFuture<Void> taskB = CompletableFuture.runAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());}).thenRunAsync(() -> { // 拼接异步任务,不获取前面任务的返回值System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());}, executor);}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 222222
线程B名字: pool-1-thread-1
小结:用法和thenApplyAsync,thenAcceptAsync一样,都是任务A和任务B的拼接,前置任务没有返回结果,后置任务不接收前置任务结果,后置任务也没有返回结果。
thenCombine
示例代码:
public static void main(String[] args) {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";}).thenCombine(CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());return "返回结果 2222222";}), (r1, r2) -> {System.out.println("执行了吗 3333333");System.out.println("线程C名字: " + Thread.currentThread().getName());return r1 + r2;});String result = future.join();System.out.println("线程C结果: " + result);}
运行结果:
执行了吗 111111
执行了吗 222222
线程B名字: ForkJoinPool.commonPool-worker-2
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 3333333
线程C名字: main
线程C结果: 返回结果 1111111返回结果 2222222
小结:如有任务A,任务B,任务C,任务A和任务B并行执行,等到任务A和任务B全部执行完毕后,再执行任务C,当前方式前置任务需要有返回结果,后置任务接收前置任务的结果,有返回值
thenAcceptBoth
示例代码:
public static void main(String[] args) {CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());return "返回结果 2222222";}), (r1, r2) -> {System.out.println("执行了吗 3333333");System.out.println("线程C名字: " + Thread.currentThread().getName());String result = r2 + r1;System.out.println("线程C结果: " + result);});}
运行结果:
执行了吗 111111
执行了吗 222222
线程B名字: ForkJoinPool.commonPool-worker-2
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 3333333
线程C名字: main
线程C结果: 返回结果 2222222返回结果 1111111
小结:如有任务A,任务B,任务C,任务A和任务B并行执行,等到任务A和任务B全部执行完毕后,再执行任务C,当前方式前置任务需要有返回结果,后置任务接收前置任务的结果,没有返回值
runAfterBoth
示例代码:
public static void main(String[] args) {CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());return "返回结果 1111111";}).runAfterBoth(CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());return "返回结果 2222222";}), () -> {System.out.println("执行了吗 3333333");System.out.println("线程C名字: " + Thread.currentThread().getName());});}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 222222
线程B名字: ForkJoinPool.commonPool-worker-1
执行了吗 3333333
线程C名字: main
小结:如有任务A,任务B,任务C。任务A和任务B并行执行,等到任务A和任务B全部执行完毕后,再执行任务C,当前方式前置任务不需要有返回结果,后置任务不会接收前置任务的结果,没有返回值
allOf
示例代码1:
public static void main(String[] args) throws IOException {CompletableFuture.allOf(CompletableFuture.runAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行结束了吗 111111");}),CompletableFuture.runAsync(() -> {System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行结束了吗 222222");}),CompletableFuture.runAsync(() -> {System.out.println("执行了吗 33333333");System.out.println("线程C名字: " + Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行结束了吗 3333333");})).thenRun(() -> {System.out.println("执行了吗 44444444");System.out.println("线程D名字: " + Thread.currentThread().getName());});}
运行结果1:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 222222
线程B名字: ForkJoinPool.commonPool-worker-2
执行了吗 33333333
线程C名字: ForkJoinPool.commonPool-worker-3
执行了吗 44444444
线程D名字: main
示例代码2:
public static void main(String[] args) throws ExecutionException, InterruptedException {List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 任务的返回值List<CompletableFuture<Integer>> futures = nums.stream()// 任务提交 带返回值类型.map(value -> CompletableFuture.supplyAsync(() -> {System.out.println("任务名称: " + Thread.currentThread().getName() + " value: " + value);return value + 1;})).collect(Collectors.toList());// 等 allOf 里面的任务都执行完成之后在执行thenApplyAsync后面拼接的任务// 注意:allOf本身没有返回值,但是后面拼接的任务有返回值,此返回值取得是后面拼接任务的返回值CompletableFuture<Integer> sumFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> {// 拼接任务,处理异步计算任务完成后,将它们的结果进行合并int sum = futures.stream().mapToInt(CompletableFuture::join).sum();return sum;});// 阻塞获取结果int sum = sumFuture.join();System.out.println(sum);// 如果不阻塞获取结果,则异步任务执行不影响main方法执行,使用join阻塞获取结果则会等待上述任务执行结束才会执行后面流程System.out.println("11111111111111111111");System.out.println("11111111111111111111");System.out.println("11111111111111111111");}
运行结果2:
任务名称: ForkJoinPool.commonPool-worker-1 value: 1
任务名称: ForkJoinPool.commonPool-worker-1 value: 3
任务名称: ForkJoinPool.commonPool-worker-2 value: 2
任务名称: ForkJoinPool.commonPool-worker-1 value: 4
任务名称: ForkJoinPool.commonPool-worker-1 value: 6
任务名称: ForkJoinPool.commonPool-worker-1 value: 7
任务名称: ForkJoinPool.commonPool-worker-2 value: 8
任务名称: ForkJoinPool.commonPool-worker-1 value: 9
任务名称: ForkJoinPool.commonPool-worker-2 value: 10
任务名称: ForkJoinPool.commonPool-worker-3 value: 5
65
11111111111111111111
11111111111111111111
11111111111111111111
小结:让内部编写多个CompletableFuture的任务,多个任务都执行完后,才会继续执行你后续拼接的任务,返回的CompletableFuture是Void,没有返回结果。
anyOf
示例代码:
public static void main(String[] args) throws IOException {CompletableFuture.anyOf(CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 111111");System.out.println("线程A名字: " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行结束了吗 111111");return "线程A";}),CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 222222");System.out.println("线程B名字: " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行结束了吗 222222");return "线程B";}),CompletableFuture.supplyAsync(() -> {System.out.println("执行了吗 33333333");System.out.println("线程C名字: " + Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行结束了吗 3333333");return "线程C";})).thenAccept(r -> {System.out.println("执行了吗 4444444");System.out.println("线程D名字: " + Thread.currentThread().getName());System.out.println("result: " + r);});}
运行结果:
执行了吗 111111
线程A名字: ForkJoinPool.commonPool-worker-1
执行了吗 222222
线程B名字: ForkJoinPool.commonPool-worker-2
执行了吗 33333333
执行了吗 4444444
线程D名字: main
小结:让内部编写多个CompletableFuture的任务,只要有一个任务执行完毕就继续执行后续,最先执行完的任务做作为返回结果的入参。