前言
Java是如何实现和管理线程池的?
从JDK 5开始,Java 把工作单元与执行机制分离开来,工作单元包括 Runnable 和 Callable,而执行机制由Executor框架提供。
1. 为什么要有线程池
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
2. Execute 框架
2.1 线程池执行原理
2.2 Executor 框架结构(主要由三大部分组成)
1)任务(Runnable /Callable)
执行任务需要实现的 Runnable 接口 或 Callable 接口。Runnable 接口或 Callable 接口 实现类都可以被 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。
2)任务的执行(Executor)
如下图所示,包括任务执行机制的核心接口 Executor ,以及继承自 Executor 接口的 ExecutorService 接口。ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 这两个关键类实现了 ExecutorService 接口。
这里提了很多底层的类关系,但是,实际上我们需要更多关注的是 ThreadPoolExecutor 这个类,这个类在我们实际使用线程池的过程中,使用频率还是非常高的。
3)异步计算的结果(Future)
Future 接口以及 Future 接口的实现类 FutureTask 类都可以代表异步1计算的结果。
当我们把 Runnable 接口 或 Callable 接口 的实现类提交给 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。(调用 submit() 方法时会返回一个 FutureTask 对象)
3. Exexutors 工厂类
提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口
因为ThreadPoolExecutor的参数众多且意义重大,为了避免配置出错,才有了Executors工厂类
在《阿里巴巴 Java 开发手册》“并发处理”这一章节,明确指出线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式。这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
Executors 返回的线程池允许创建的线程数量为 Integer.MAX_VALUE
,可能会创建大量线程,从而导致 OOM。