张治峰的博客

线程池初探

2021-07-25

简介

线程池”,顾名思义就是一个线程缓存。线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,因此Java中提供线程池对线程进行统一分配、调优和监控。

优势

池化技术相比大家已经屡见不鲜了,线程池、数据库连接池、Http 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。

线程池的好处有:

  1. 复用存在的线程,减少线程创建,销毁的开销,提高性能
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

使用场景

  1. 单个任务处理时间比较短
  2. 需要处理的任务数量很大

线程池一般用于执行多个不相关联的耗时任务,没有多线程的情况下,任务顺序执行,使用了线程池的话可让多个不相关联的任务同时执行以提高效率。

假设我们执行n个不相关的耗时任务如下图可以提高执行效率

任务对比

Executor 框架

简介

Executor框架是 Java5 之后引进的,其内部使用了线程池机制,通过该框架来控制线程的启动、执行和关闭。

通过 Executor 来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题[^RUNOOB]。

补充:this 逃逸是指在构造函数返回之前其他线程就持有该对象的引用. 调用尚未构造完全的对象的方法可能引发令人疑惑的错误。

Executor框架不仅包括了对线程池的管理,还提供了线程工厂、任务队列以及拒绝策略等,Executor 框架让并发编程变得更加简单。

框架结构

任务(Runnable/Callable)

任务,也就是工作单元,执行的任务需要实现Runnable接口或者Callable接口

任务的执行(Executor)

把任务分派给多个线程的执行机制,包括Executor接口及继承自Executor接口的ExecutorService接口。ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 这两个关键类实现了 ExecutorService 接口。

我们主要关注ThreadPoolExecutor这个类。这个类在实际使用线程池中的频率是非常高的, 类图如下
类图

异步执行结果(Future)

Future 接口以及 Future 接口的实现类 FutureTask 类都可以代表异步计算的结果。

当我们把 Runnable接口Callable接口的实现类提交给 ThreadPoolExecutorScheduledThreadPoolExecutor 执行。(调用 submit() 方法时会返回一个 FutureTask 对象)

Executor 使用示意图

类图

  1. 主线程创建实现 Runnable 或者 Callable 接口的任务对象。
  2. 将上一步的对象交给 ExecutorService 执行 ExecutorService.execute(Runnable command) 或者 ExecutorService.submit(Runnable task) ExecutorService.submit(Callable task)
  3. 如果上步骤执行的是 ExecutorService.submit() 方法将异步返回一个 FutureTask对象,这也是submit 和 execute执行任务的区别。
  4. 主线程可以执行 FutureTask.get() 方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。

ThreadPoolExecutor 类简单使用

线程池实现类 ThreadPoolExecutor 是 Executor 框架最核心的类。

ThreadPoolExecutor 源码解析

后期单独进行讲解

Tags: java
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章