JAVA多线程之CompletableFuture类

288 阅读3分钟

JAVA多线程使用

  1. 多线程是一种用来提升代码执行效率的方式。单线程上本该阻塞等待的操作,多线程可以通过并行来节省时间。
  2. Java的多线程通过继承Thread类或实现Runnable,Future接口来实现。

多线程的注意事项

参考阿里巴巴的《JAVA开发手册》

  1. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
    • 线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用 线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
  2. 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方 式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    • Executors 返回的线程池对象的弊端如下:
    1)FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 
    2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
    3)ScheduledThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    

开发中常用的异步编程方式

  1. 在springboot框架下的异步,通过异步注解@Async("asyncExecutor")并指定注入的线程池名(默认的线程池存在OOM风险)
  2. 通过JAVA8提供的CompletableFuture进行异步编排。

CompletableFuture类(常用)

1. 方法命名规则

  • 带有Async后缀方法都是异步另外线程执行,没有就是复用之前任务的线程
  • 带有Apply标识方法都是可以获取返回值+有返回值的
  • 带有Accept标识方法都是可以获取返回值
  • 带有run标识的方法不可以获取返回值和无返回值,只是运行 2. 异步执行某任务无返回值
CompletableFuture future = CompletableFuture.runAsync(() -> {
    for (int i = 0; i < 5; i++) {
        System.out.println("Hello " + i);
    }
}, executor);

3. 异步执行某任务带返回值

CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("带返回值的异步任务");
    return "Hello";
},executor);

4. 等前线程执行完后开始执行

future.thenRunAsync(() -> {
    System.out.println("在future2执行完成后执行");
},executor);

5. 等前线程执行完后获取返回值并执行

future2.thenAcceptAsync(s -> {
    System.out.println("获取返回值: " + s);
},executor);

6. 等前线程执行完后获取返回值并执行且带返回值

future2.thenApplyAsync(s -> {
    System.out.println("获取返回值: " + s);
    return s + " world";
},executor)

7. 等待全部完成后才执行

CompletableFuture allOf = CompletableFuture.allOf(future, future2);

8. 等待其中之一完成后就执行

CompletableFuture anyOf = CompletableFuture.anyOf(future, future2);

9. 捕获结果或异常并返回新结果

CompletableFuture future14 = CompletableFuture.supplyAsync(() -> {
    int i = 10 / 0;
    return i;
} ,executor).handleAsync((res, e) -> {
    if(e != null){
        return "出现异常";
    }
    return res;
} );

关于CompletableFuture类拓展

1. 两个任务完成后运行

CompletableFuture future7 = future5.runAfterBothAsync(future2,() -> {
    System.out.println("future5和future2执行完成后执行");
} ,executor);

2. 消费两个结果无返回值

CompletableFuture future6 = future5.thenAcceptBothAsync(future2,(s, s2) -> {
    System.out.println("future5: " + s);
    System.out.println("future2: " + s2);
} ,executor);

3. 消费两个结果带返回值

CompletableFuture future8 = future5.thenCombineAsync(future2,(s, s2) -> {
    System.out.println("future5: " + s);
    System.out.println("future2: " + s2);
    return s2;
} ,executor);

4. 有一个任务完成就执行

CompletableFuture future9 = future5.runAfterEitherAsync(future2,() -> {
    System.out.println("future5或future2执行完成后执行");
} ,executor);

5. 其中一个执行完执行并获取返回值

CompletableFuture future10 = future5.acceptEitherAsync(future2,(res) -> {
    System.out.println("future5或future2执行完成后执行:"+res);
} ,executor);

6. 其中一个执行完执行并获取返回值且有返回值

CompletableFuture future11 = future5.applyToEitherAsync(future2,(res) -> {
    System.out.println("future5或future2执行完成后执行:"+res);
    return res;
} ,executor);

CompletableFuture类异常处理

1. 捕获异常并返回指定值

CompletableFuture future12 = CompletableFuture.supplyAsync(() -> {
    int i = 10 / 0;
    return i;
} ,executor).exceptionally(e -> {
    System.out.println("异常处理"+e);
    return 0;
});

2. 感知结果或异常并返回相应信息(不能修改返回值)

CompletableFuture future13 = CompletableFuture.supplyAsync((() -> {
    int i = 10 / 0;
    return i;
}),executor).whenComplete((res, e) -> {
    System.out.println("异常处理 res:"+res + " e:"+e);
} );