Java 线程池技术属于比较古老而又比较基础的技术了, 本篇博客主要作用是个人技术梳理, 没什么新玩意
一 Java 线程池技术的由来
我们平时使用线程来进行异步操作时, 线程的创建, 销毁等相对来说都是比较消耗资源的, 试想这样一个业务情景: 高并发请求, 但是每次请求的时间非常短如果我们为每一个请求都单独创建一个线程来执行, 就会消耗大量设备资源, 使设备处于高负荷状态, 显然这样的处理就有很大问题了这时候我们就可以用线程池技术来解决了, 我们在线程池中创建若干条线程, 当有任务需要执行时就从该线程池中获取一个线程来执行任务, 如果一时间任务过多, 超出线程池的线程数量, 那么后面的线程任务就进入一个等待队列进行等待, 直到线程池有线程处于空闲时才从等待队列获取要执行的任务进行处理, 这样就减少了线程创建和销毁的开销, 实现了线程的复用
二 Executor 框架介绍
首先对整体框架有个大概了解, 如图:
Executor 是个接口, 就定义了一个 void execute(Runnable command) 方法
ExecutorService 同样是一个接口并且继承自 Executor 接口, 对其方法进行扩展, 其中最重要的是 < T> Future<T> submit(Callable<T> task) 方法, 关于 Callable,Future,FutureTask 不是本篇重点, 有时间会单独写一篇博客介绍也可以自行搜索了解, 比较简单
AbstractExecutorService 抽象类, 实现了 ExecutorService 接口, 主要实现了 submit,ivokeAny 方法
ScheduledExecutorService 同样是一个接口, 继承自 ExecutorService 接口, 对其进行扩展, 主要就是 schedule 等方法
ThreadPoolExecutor 具体线程池实现类, 继承自 AbstractExecutorService 抽象类, 我们使用的时候大部分就是使用这个类, 后面会具体讲到
ScheduledThreadPoolExecutor 具有调度能力的线程池实现类, 继承自 ThreadPoolExecutor 类, 且实现 ScheduledExecutorService 接口, 其主要功能就是调用 schedule 方法, 可以延时或者周期的执行某一任务, 而 ThreadPoolExecutor 是没有这一共能的
三 ThreadPoolExecutor 构造函数参数介绍
在我们使用 ThreadPoolExecutor 的时候会发现构造函数中有很多参数, 如下:
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), defaultHandler);
- }
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- threadFactory, defaultHandler);
- }
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- RejectedExecutionHandler handler) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), handler);
- }
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,
- RejectedExecutionHandler handler) {
- if (corePoolSize < 0 ||
- maximumPoolSize <= 0 ||
- maximumPoolSize < corePoolSize ||
- keepAliveTime < 0)
- throw new IllegalArgumentException();
- if (workQueue == null || threadFactory == null || handler == null)
- throw new NullPointerException();
- this.corePoolSize = corePoolSize;
- this.maximumPoolSize = maximumPoolSize;
- this.workQueue = workQueue;
- this.keepAliveTime = unit.toNanos(keepAliveTime);
- this.threadFactory = threadFactory;
- this.handler = handler;
- }
前 3 个构造函数都是调用第 4 个构造函数, 只不过有些参数使用默认的罢了
接下来我们看下第 4 个构造函数每个参数意义:
- public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,RejectedExecutionHandler handler)
参数 | 说明 |
corePoolSize | 线程池中核心线程数量 |
maximumPoolSize | 线程池中最大线程数量 |
keepAliveTime | 非核心线程存活时间 |
unit | keepAliveTime 的时间单位 |
workQueue | 存放任务的队列 |
threadFactory | 用来生产线程的工厂 |
handler | 当线程池中不能再放入任务时执行的 handler |
如果有一个 corePoolSize 为 5,maximumPoolSize 为 10 的线程池, 可用下图形象展示:
这里要说明一下: 所谓核心线程非核心线程只是一个数量的说明, 并不是说
核心线程非核心线程
有本质上的不同, 它们都是普通的线程而已, 并且线程特性都一样, 不是说核心线程有特殊标记, 线程池能认出来这是核心线程, 对其有特殊操作
四线程池处理任务的策略
我们调用线程池的 submit() 或 execute() 方法, 向线程池中放入一个任务执行的时候线程池到底是怎么按照其策略来执行的呢? 接下来, 我们对其执行策略进行详细介绍, 介绍完会对构造函数中每个参数有更深刻印象的
1, 调用线程池的 submit() 或 execute() 方法向线程池中放入一个任务, 线程池内部会检查运行的线程数量是否达到 corePoolSize 数量, 如果没有达到, 则创建一个线程执行放入的任务, 不管已经创建的线程是否处于空闲状态, 创建线程的任务由 threadFactory 来完成, 关于 ThreadFactory 可以参考我的另一篇博客来学习: java 线程池技术 (一):ThreadFactory 与 BlockingQueue
2, 我们继续向线程池中放入任务, 此时线程池中运行的线程数量已经达到 corePoolSize 数量, 则新加入的任务将会被放入 workQueue 中, 直到有线程处于空闲状态, 则从 workQueue 中取出任务执行
3, 继续向线程池中放入任务,
此时线程池中运行的线程数量已经达到
corePoolSize 数量,
并且
workQueue
中已经放满任务不能再放入新的任务, 那么这时候就继续创建新的线程, 注意此时线程池中线程数量已经多余
corePoolSize 数量
, 多出来的线程就叫做非核心线程用非核心线程来执行新放入的任务当线程池中的线程超过你设置的 corePoolSize 参数, 说明当前线程池中有所谓的非核心线程当某个线程处理完任务后, 如果等待 keepAliveTime 时间后仍然没有新的任务分配给它, 那么这个线程将会被回收线程池回收线程时, 对所谓的核心线程和非核心线程是一视同仁的, 直到线程池中线程的数量等于你设置的 corePoolSize 参数时, 回收过程才会停止
4, 继续向线程池中放入任务, 此时线程池中运行的线程数量已经达到 maximumPoolSize 数量, 并且
workQueue 中已经放满任务不能再放入新的任务, 由于线程池中运行的线程
已经达到 maximumPoolSize 数量, 所以无法再创建线程执行新放入的任务, 此时 handler 参数就起作用了, 在使用的时候相信大部分开发者都没用过这个参数, 我们看下系统默认怎么处理的,
系统默认闯入传入的是 defaultHandler, 如下:
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- threadFactory, defaultHandler);
- }
初始化如下:
- private static final RejectedExecutionHandler defaultHandler =
- new AbortPolicy();
接下来看下 AbortPolicy 这个类吧:
- public static class AbortPolicy implements RejectedExecutionHandler {
- /**
- * Creates an {@code AbortPolicy}.
- */
- public AbortPolicy() { }
- /**
- * Always throws RejectedExecutionException.
- *
- * @param r the runnable task requested to be executed
- * @param e the executor attempting to execute this task
- * @throws RejectedExecutionException always
- */
- public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
- throw new RejectedExecutionException("Task" + r.toString() +
- "rejected from" +
- e.toString());
- }
- }
AbortPolicy 实现了 RejectedExecutionHandler 接口, 在 rejectedExecution 方法中抛出 RejectedExecutionException 异常
所以如果线程池中运行的线程数量已经达到 maximumPoolSize 数量, 并且 workQueueworkQueue 中已经放满任务不能再放入新的任务, 系统默认情况下就会抛出
RejectedExecutionException 异常, 我们也可以自己实现 RejectedExecutionHandler 接口, 在 rejectedExecution 方法中实现自己策略比如我自己写的网络请求框架就自己定义了
RejectedExecutionHandler, 如下:
- public class RejectedPolicy implements RejectedExecutionHandler{
- @Override
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
- try {
- taskQuene.put(r);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
好了, 以上就是线程池具体执行一个新任务的大体策略, 是不是有了更深的认识???
以上分析中涉及的 ThreadFactory 与 BlockingQueue 如果你不是太了解, 可以参考我的另一篇博客了解一下:
java 线程池技术 (一):ThreadFactory 与 BlockingQueue
好了, 关于线程池大体介绍就到此为止, 希望对你有用
来源: https://www.cnblogs.com/leipDao/p/8508093.html