张治峰的博客

线程池初探

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接口的实现类提交给 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。(调用 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
赏
使用支付宝打赏
使用微信打赏

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

扫描二维码,分享此文章