线程池 ThreadPoolExecutor 源码分析基础 - 线程池工作原理

前言

本文主要内容为分析 ThreadPoolExecutor 线程池的执行原理,然后前后会穿插一些线程池的其他相关内容(构造方法,线程池状态 等等),本文最终目的是为深入分析 ThreadPoolExecutor 源码打下基础。

阅读前请对线程池的概念及作用有所了解(本文不会涉及)。

另外,本文主要内容基于 jdk1.8 来说明。

正文

大纲

  • 线程池的构造方法
  • 线程池的创建方式
  • 线程池的工作状态
  • 线程池的工作原理
  • 线程池 Worker 内部类

ThreadPoolExecutor 的构造方法

ThreadPoolExecutor总共有四个构造方法,这里只贴出一个,因为其他三个构造最终还是会调用到此方法

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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
            
            
    // 参数初始化
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
构造参数详解

先对每个字段的含义有个大概印象,后面 线程池原理 部分会把这些参数串起来讲。

  1. int corePoolSize

    线程池核心线程数量

  2. int maximumPoolSize

    线程池最大线程数量

  3. long keepAliveTime

    线程池中大于 corePoolSize 的那部分线程(处于闲置状态)的最大存活时间

  4. TimeUnit unit

    存活时间单位

  5. BlockingQueue workQueue

    保存等待执行的任务的一个阻塞队列

  6. ThreadFactory threadFactory

    创建线程的一个工厂, 默认为DefaultThreadFactory类

  7. RejectedExecutionHandler handler

    线程饱和策略,默认为ThreadPoolExecutor.AbortPolicy。

创建线程池的方式

  1. 通过 ThreadPoolExecutor 的构造函数创建

  2. Executors.newCachedThreadPool()

创建一个可缓存的无界线程池,该方法无参数。当线程池中的线程空闲时间超过60s则会自动回收该线程,当任务超过线程池的线程数则创建新线程。线程池的大小上限为Integer.MAX_VALUE,可看做是无限大。

  1. Executors.newFixedThreadPool()

创建一个固定大小的线程池,该方法可指定线程池的固定大小,对于超出的线程会在LinkedBlockingQueue队列中等待。

  1. Executors.newSingleThreadPool()

创建一个只有线程的线程池,该方法无参数,所有任务都保存队列LinkedBlockingQueue中,等待唯一的单线程来执行任务,并保证所有任务按照指定顺序(FIFO或优先级)执行。

  1. Executors.newScheduledThreadPool()

创建一个可定时执行或周期执行任务的线程池,该方法可指定线程池的核心线程个数。

线程池的工作状态

线程池的状态用 int 类型的值表示,具体表示形式 Java 线程池 ThreadPoolExecutor 中的位运算操作 一文中有详细讲到。

分别以下几种:

  • RUNNING

    可以接收新增的任务,并处理阻塞队列中的任务

  • SHUTDOWN

    拒绝新增的任务,但是可以处理阻塞队列中的任务

  • STOP

    拒绝新增的任务,并拒绝阻塞队列中的任务,同时会中断正在处理的任务

  • TIDYING

    表示所有的任务都执行完了,线程池中活跃的线程数为 0,即将调用 terminated 方法

  • TERMINATED

    terminated() 方法调用后的状态

它们的值由小到大依次为:

RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED

因此当我们看类似到如下表达式时,就应该知道 ws 的状态是什么。


    // ws == RUNNING
    if(ws < SHUTDOWN) {
        // TODO 
    }
    

线程池工作状态切换

  • RUNNING -> SHUTDOWN
    当调用 shutdown() 方法,或者间接调用 finalize() 方法时

  • RUNNING or SHUTDOWN -> STOP
    当调用 shutdownNow() 方法

  • SHUTDOWN -> TIDYING
    当队列和线程池都为空时

  • STOP -> TIDYING
    当线程池为空时

  • TIDYING -> TERMINATED
    当 terminated() hook 方法执行完毕后

线程池的工作原理

线程池的工作原理要结合它的构造参数来说明。

  1. 当前线程池的线程个数小于 corePoolSize时,开启新的核心线程执行
  2. 如果线程池处于 Running 状态,则添加任务到阻塞队列 workQueue
  3. 如果阻塞队列 workQueue 满了,就新增一个非核心线程新增(失败了就执行具体的拒绝策略)。

大致流程入如下:

线程池的工作原理

Worker 内部类

说 Worker 类之前,先来复习下如何创建并启动一个线程。


// 1. 实现 Runnbale 接口
class MyTask implements Runnable {
    
    // 2. 重写 run 方法
    void run(){
        // doSomething
    }
}

// 3. new 一个 Thread,把 MyTask 对象作为参数传进去
Thread thread = new Thread(new MyTask());

// 4. 调用 start() 方法, 最终会去调用 MyTask#run() 方法
thread.start();

而我们的 Worker 类的作用跟上面的 MyTask 是一样的,只不过它的内容更丰富, 来看下 Worker 类的定义。


class Worker extends AbstractQueuedSynchronizer implements Runnable {
       
        final Thread thread;
       
        Runnable firstTask;
        
        Worker(Runnable firstTask) {
             
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        
        // 省略部分代码
        
}

大概讲一下 Worker 在线程池中的作用,把上面几个点串起来:

// 1. new 一个上面定义的 task
MyTask mytask = new MyTask();

// 2. 创建一个 Worker 对象
//  2.1 把 mytask 赋值给了 firstTask 属性
//  2.2 通过线程工厂创建一个 Thread 对象,赋值给 thread
//  2.3 thread 构造参数为 this,也就是 worker(因为 Worker 也实现了 Runnable 接口)
Worker worker = new Worker(mytask);


// 3. 拿到 worker 对象中的 thread 对象
Thread thread = worker.thread;
// 4. 启动一个线程,最终会调用 worker 对象的 run 方法
thread.start();

而我们的 worker#run 方法是个死循环,它主要做的其中一件事情就是执行 firstTaskrun 方法,另一件事情就是执行阻塞队列 workQueue 中的任务。

具体如何执行的后面会结合源码深入了解。

总结

本文作为线程池源码分析的基础篇部分,主要内容是对 线程池的工作原理进行初步分析,该部分内容暂时讲得比较浅,后面 具体源码分析 时会深入探究。

除此之外,穿插了以下内容:

  • ThreadPoolExecutor 的构造参数
  • 线程池的 几种创建方式
  • 线程池的 几种工作状态
  • 线程池工作状态切换
  • 线程池内部类· Worker 的简单介绍
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值