动态线程池解析与CompletableFuture类
一、动态线程池的需求
我们都知道线程池的引入,使得线程不再频繁创建与销毁,节省了系统的性能。线程池的引入,让线程得到复用,响应速度也更快。当我们使用ThreadPoolExecutor创建线程池时,需要指定参数,那么我们创建的线程池的规模一般是固定的。然而现实生活中,需求总是在变化的,来自用户的流量也是时刻在变化。动态线程池可以根据流量的不同,从而调节线程池中的某些参数。在流量洪峰时增加线程,在流量低谷时减少线程。动态线程池,可以适应并发量的突发性,以及提高系统的可伸缩性。
二、动态线程池的基础
通过ThreadPoolExecutor创建线程池,corePoolSize等参数确定线程池的特性。JDK中的ThreadPoolExecutor类提供了多个参数的set方法,如下图所示:
通过set方法,可以调整线程池的核心线程数,最大线程数,存活时间,线程工厂和拒绝策略。这些public的set方法是动态线程池实现的基础。
三、动态线程池的实现
虽然上述的set方法,可以实现对线程池参数的更改,但每次修改都需要重新发布,这是一件非常麻烦的事情。我们可以考虑将线程池中的参数从代码中迁移到分布式配置中心中,基于配置中心对线程池ThreadPoolExecutor做一些扩展,实现对运行中线程池参数的动态修改,实时生效。Apollo是携程框架部门研发的分布式配置中心,能够集中管理应用不同集群的配置,在配置修改后能够实时推送到应用端。下面说一下使用的步骤:
- 下载Apollo并整合SpringBoot,导入Maven依赖,配置application.yml文件,启动类上加@EnableApolloConfig。
- 在Apollo服务端发布一个线程池配置。
- 编写核心代码类。
- 线程测试执行。
四、CompletableFuture
4.1 Future
CompletableFuture是Future的增强,要明白CompleteFuture,得先了解Future。Future是对Runnable或Callable任务结果进行取消、查询状态、获取结果。在实际中,使用FutureTask创建Future。将 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,放到线程池中或另起线程去执行,最后还可以通过 FutureTask 获取任务执行的结果,代码如下所示:
//子线程任务
public class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("线程正在计算");
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
}
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTask task = new CallableTask();
//构建futureTask
FutureTask<Integer> futureTask = new FutureTask<>(task);
//作为Runnable入参
new Thread(futureTask).start();
System.out.println("task运行结果:" + futureTask.get());
}
}
执行结果:
4.2 CompletableFuture
事实上,Future表示一个异步计算的结果。在异步计算中,Future确实是个非常好用的接口。简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,很多时候业务逻辑处理存在串行、并行、聚合的关系。如果要我们手动用 Fueture 实现,是非常麻烦的。CompletableFuture是jdk8的新特性,实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。它能够创建异步任务、进行异步回调处理、也可以进行多任务组合处理,常用方法如下图所示:
这里展示一个多任务处理的例子,使用thenCombine方法,将任务1(cf1)与任务2(cf2)联合起来,进行任务3(cf3),代码如下:
public class CompleteFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " 任务1运行中....");
return 2;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " 任务2运行中....");
return 3;
});
CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2, (a, b) -> {
System.out.println(Thread.currentThread() + " 任务3运行中....");
return a + b;
});
System.out.println("cf3结果->" + cf3.get());
}
}
执行结果: