Runnable和Callable

都是代表一个任务,可以把这个任务交给线程或者线程池执行

  • 都是函数式接口,可以用lambda表达式替代
  • Runnable没有返回值,任务内不抛出异常
  • Callable有返回值,任务内可以抛出异常,Callable需要用FutureTask包装才能交给线程执行
1
2
3
4
5
@FunctionalInterface
public interface Runnable {

public abstract void run();
}
1
2
3
4
5
@FunctionalInterface
public interface Callable<V> {

V call() throws Exception;
}

Callable和FutureTask

FutureTask实现了Runnable接口,Callable需要用FutureTask包装就可以交给线程执行

同时FutureTask是实现了Future接口,有get()方法等待异步任务结果,用了类似于Condiction条件队列的虚拟链表方式存储需要返回结果的任务

image-20240507194707164

任务的状态

任务状态是不可逆的,初始任务在构造器里面就是NEW状态

和AQS一样,有一个state变量存储任务状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private volatile int state;

// 任务新建状态
private static final int NEW = 0;

// 终止中间状态
private static final int COMPLETING = 1;

// 任务正常结束
private static final int NORMAL = 2;

// 任务出现异常
private static final int EXCEPTIONAL = 3;

// 任务被取消
private static final int CANCELLED = 4;

// 中断中间状态
private static final int INTERRUPTING = 5;

// 执行者线程被中断
private static final int INTERRUPTED = 6;

状态转换图

image-20240507194952102

CompletableFuture

在之前的FutureTask中,如果想要获取到多线程执行的结果,有两种办法,一种是轮询FutureTask.isDone()方法,当结果为true的时候获取执行结果,第二种则是调用FutureTask.get()方法。但是无论那种方式都无法实现真正意义上的异步回调,因为任务执行需要时间,所以都会使得主线程被迫阻塞。

CompletableFuture对象实现异步任务

image-20240509191515155

CompletableFuture也实现了Future接口,所以和FutureTask一样也可以用complete()完成异步任务,并且用get()阻塞等待。

CompletableFuture的静态方法实现异步任务

为了能够真正的实现异步通过回调代替阻塞,可以用CompletableFuture的静态方法:

  • runAsync和supplyAsync都支持用线程池进行任务调度,如果不为CompletableFuture指定线程池执行任务的情况下,默认是使用ForkJoinPool.commonPool()的线程,而ForkJoinPool.commonPool()的线程是作为main线程的守护线程进行的,如果main挂了,执行异步任务的线程也会随之终止结束,并不会继续执行异步任务
  • runAsync没有返回值,而supplyAsync有返回值
1
2
3
4
5
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor);
public static <U> CompletableFuture<U> completedFuture(U value);

CompletableFuture成员方法实现异步任务

如果有Async后缀,分为以下3种情况:

  • 上一个任务已经执行结束了,那么当前创建出的任务会交给上个任务的执行线程来执行
  • 上一个任务还没有执行结束,那么则会另启一条线程来执行
  • 如果创建任务时指定了执行线程池,则会使用指定线程池的线程来执行

一般有4种成员方法:因为CompletableFuture实现了CompletionStage接口,在每个任务执行完成后又回返回一个CompletableFuture对象,使用时我们可以接着基于该对象继续创建新的任务,可以链式调用

  • thenApply()):此类方法可以基于上一个任务再创建一个新的有返回型任务。
  • handle():与thenApply类作用相同,不同点在于thenApply类方法只能在上一个任务执行正常的情况下才能执行,当上一个任务执行抛出异常后则不会执行。而handle类在上个任务出现异常的情况下也可以接着执行
  • thenRun():此类方法可以基于上一个任务再创建一个新的无返回型任务。
  • thenCompose():与thenApply类大致相同,不同点在于每次向下传递都是新的CompletableFuture对象,而thenApply向下传递的都是同一个CompletableFuture对象对象

异步回调和异常处理

  • 主线程调用直接阻塞获取执行结果get、getNow方法
  • 可以为无返回值的异步任务写出执行结果的:complete开头的方法
  • 任务正常执行成功的回调:thenAccept开头的方法
  • 任务执行抛出异常的回调:exceptionally方法
  • 任务执行结束的回调:whenComplete开头的方法

调用时序

串行

thenApplythenAcceptthenRunhandle以及thenCompose

并行

汇聚

AND类型:全部异步任务都需要完成

  • thenCombine系列:可以接收前面任务的结果进行汇聚计算,并且计算后可以返回值
  • thenAcceptBoth系列:可以接收前面任务的结果进行汇聚计算,但计算后没有返回值
  • runAfterBoth系列:不可以接收前面任务的结果且无返回,但可以在任务结束后进行汇聚计算
  • CompletableFuture类的allOf系列:不可接收之前任务的结果,但可以汇聚多个任务,但是要配合回调处理方法一起使用

OR类型:只需要一个异步任务就行

  • applyToEither系列:接收最先完成的任务结果进行处理,处理完成后可以返回值
  • acceptEither系列:接收最先完成的任务结果进行处理,但是处理完成后不能返回
  • runAfterEither系列:不能接收前面任务的返回值且无返回,单可以为最先完成的任务进行后继处理
  • CompletableFuture类的anyOf系列:可以同时汇聚任意个任务,并接收最先执行完成的任务结果进行处理,处理完成后没有返回值,需要配合回调方法一起使用