LZ目前正在做一个批量生成报表的系统,需要定时批量生成多张报表,便考虑使用线程池来完成。JDK自带的Executors工具类只提供创建固定线程和可伸展但无上限的两个静态方法,并不能满足LZ想自定制线程池大小的要求。于是就直接深入了解下ThreadPoolExecutor类,以方便在工作中灵活使用以及为以后的扩展打下基础。

java doc中对ThreadPoolExecutor的说明是:

An ExecutorService that executes each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods.

一个使用线程池来执行提交的任务的ExecutorService子类,正常通过Executors工具类中的工厂方法进行配置。

那我们就先看一下比较熟悉的Executors中的几个方法的实现代码:

Executors.newCachedThreadPool

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}

Executors.newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}

Executors.newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}

可以看到其实这些方法都是通过构造方法创建了ThreadPoolExecutor对象,我们来看下具体的构造方法实现

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}

这里我们可以看到ThreadPoolExecutor中比较重要的一些参数,这些参数都是可以通过外部传入,对ThreadPoolExecutor内部进行控制。而ThreadPoolExecutor内部的工作机制究竟是怎样进行的呢?下面我们就揭开它的外衣,深入其中仔细探究。

1.ThreadPoolExecutor继承了AbstractExecutorService类

public class ThreadPoolExecutor extends AbstractExecutorService

2. ThreadPoolExecutor的重要变量参数

  • ctl: 用来标识线程池状态的重要参数,很多操作执行前都需要对线程池状态进行前置判断,以确定线程池状态是否正常

  • workQueue: 任务队列,用来在全部当前线程正在处理任务时存储提交来的任务

  • works: 存储所有工作线程

  • corePoolSize: 核心线程数

  • maximumPoolSize: 最大线程数

  • keepAliveTime: 空闲线程等待任务时间

  • threadFactory: 线程创建工厂

  • handler: 因线程池饱和或关闭触发的拒绝异常处理器

      //标识线程池控制状态
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); //线程池状态类型
    //接受新的任务并处理队列中的任务
    private static final int RUNNING= -1 << COUNT_BITS;
    //不接受新任务但处理队列中的任务
    private static final int SHUTDOWN = 0 << COUNT_BITS;
    //不接受新任务也不处理队列中的任务,且中断正在进行的任务
    private static final int STOP = 1 << COUNT_BITS;
    //所有任务已经完结,工作线程数为0,并调用terminated方法
    private static final int TIDYING= 2 << COUNT_BITS;
    //terminated方法执行完成
    private static final int TERMINATED = 3 << COUNT_BITS;
    //任务队列,储存任务以提供给工作线程
    private final BlockingQueue<Runnable> workQueue;
    //主要锁,设置workers和相关数据记录调用
    private final ReentrantLock mainLock = new ReentrantLock();
    //存储所有工作线程,设置时需要加mainLock锁
    private final HashSet<Worker> workers = new HashSet<Worker>();
    //线程池已达到的最大数,设置时需要加mainLock锁
    private int largestPoolSize;
    //已完成任务数,设置时需要加mainLock锁
    private long completedTaskCount;
    //线程创建工厂
    private volatile ThreadFactory threadFactory;
    //因饱和或线程池关闭触发的拒绝异常处理器
    private volatile RejectedExecutionHandler handler;
    //空闲线程等待任务时间(单位:纳秒),到时则会被销毁
    private volatile long keepAliveTime;
    //默认为false,核心线程在空闲时一直存活
    //如果为true,核心线程使用keepAliveTime参数来等待任务
    private volatile boolean allowCoreThreadTimeOut;
    //核心线程数
    private volatile int corePoolSize;
    //最大线程数
    private volatile int maximumPoolSize;
    //默认拒绝异常处理器
    private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();

3.execute方法,用户通过该方法提交任务给线程池。

处理任务分四种种情况:

  1. 如果当前工作线程数小于核心线程数,则创建新的线程来处理任务

  2. 如果当前工作线程等于核心线程数,新提交的任务存储到工作队列中

    重新检测线程池状态是否正常,如果不是运行状态,则移除任务,并处理拒绝异常

    如果线程池正常,工作线程数等于0,则增加工作线程

  3. 当工作队列达到最大容量,工作线程数没有达到最大线程数,增加新的工作线程,并处理任务

  4. 当工作线程数达到最大线程数,则使用拒绝异常处理器对任务进行处理

     public void execute(Runnable command) {
    if (command == null)
    throw new NullPointerException(); int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
    return;
    c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
    int recheck = ctl.get();
    if (! isRunning(recheck) && remove(command))
    reject(command);
    else if (workerCountOf(recheck) == 0)
    addWorker(null, false);
    }
    else if (!addWorker(command, false))
    reject(command);
    }

4.线程池是怎么增加一个新的线程的呢?

接下来我们来看addWorker方法

  1. 双重for循环检查线程池是否适合增加新的线程

  2. 创建Worker对象并获得mainLock锁

  3. 再次检查状态,防止线程工厂失败或线程池关闭

  4. works增加worker对象,并更新largestPoolSize,释放锁

  5. 启用worker对象中的线程

  6. 由于并发原因,可能会出现线程尚未执行,但线程池正在关闭,因此可能会出现线程池关闭时,错过中断当前线程,因此再进行一次判断,如果线程池状态为关闭且当前线程未被中断,则手动中断它

     private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);
    // Check if queue empty only if necessary.
    if (rs >= SHUTDOWN &&
    ! (rs == SHUTDOWN &&
    firstTask == null &&
    ! workQueue.isEmpty()))
    return false;
    for (;;) {
    int wc = workerCountOf(c);
    if (wc >= CAPACITY ||
    wc >= (core ? corePoolSize : maximumPoolSize))
    return false;
    if (compareAndIncrementWorkerCount(c))
    break retry;
    c = ctl.get(); // Re-read ctl
    if (runStateOf(c) != rs)
    continue retry;
    // else CAS failed due to workerCount change; retry inner loop
    }
    }
    Worker w = new Worker(firstTask);
    Thread t = w.thread;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    // Recheck while holding lock.
    // Back out on ThreadFactory failure or if
    // shut down before lock acquired.
    int c = ctl.get();
    int rs = runStateOf(c);
    if (t == null ||
    (rs >= SHUTDOWN &&
    ! (rs == SHUTDOWN &&
    firstTask == null))) {
    decrementWorkerCount();
    tryTerminate();
    return false;
    }
    workers.add(w);
    int s = workers.size();
    if (s > largestPoolSize)
    largestPoolSize = s;
    } finally {
    mainLock.unlock();
    }
    t.start();
    // It is possible (but unlikely) for a thread to have been
    // added to workers, but not yet started, during transition to
    // STOP, which could result in a rare missed interrupt,
    // because Thread.interrupt is not guaranteed to have any effect
    // on a non-yet-started Thread (see Thread#interrupt).
    if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
    t.interrupt();
    return true;
    }

5.Worker类的实现

在addWorker方法中,我们并没有看到任务具体执行的操作,但是可以很明显地猜测到应该是在调用t.start()方法时进行调用。而线程t是来自于Worker对象,我们来看下内部类Worker(删除了部分代码)。

  1. Worker类继承自AbstractQueuedSynchronizer,实现了Runnable接口

  2. new Worker()时,通过ThreadFactory的newThread方法创建了一个新的线程

  3. 当调用addWorker中的t.start()时,其实触发的是run方法中的runWorker(this)

     private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
    { /** Thread this worker is running in. Null if factory fails. */
    final Thread thread;
    /** Initial task to run. Possibly null. */
    Runnable firstTask;
    /** Per-thread task counter */
    volatile long completedTasks;
    /**
    * Creates with given first task and thread from ThreadFactory.
    * @param firstTask the first task (null if none)
    */
    Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
    }
    /** Delegates main run loop to outer runWorker */
    public void run() {
    runWorker(this);
    }
    }

6.runWorker方法是怎么触发任务执行的

  1. while循环保证了线程可以重复执行任务,如果firstTask执行完成后,通过getTask方法从任务队列中获取新的任务继续执行

  2. 执行前和执行后分别调用beforExecute和afterExecute两个钩子方法,可以用来在子类中自己实现,比如用于线程池监控

  3. 如果处理过程中出现意外情况,在finally中调用processWorkerExit进行处理,主要是对线程记录相关变量进行恢复,且处理当核心线程全部超时而任务队列中有新的任务时,重新增加新线程来处理任务

     final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    while (task != null || (task = getTask()) != null) {
    w.lock();
    // If pool is stopping, ensure thread is interrupted;
    // if not, ensure thread is not interrupted. This
    // requires a recheck in second case to deal with
    // shutdownNow race while clearing interrupt
    if ((runStateAtLeast(ctl.get(), STOP) ||
    (Thread.interrupted() &&
    runStateAtLeast(ctl.get(), STOP))) &&
    !wt.isInterrupted())
    wt.interrupt();
    try {
    beforeExecute(wt, task);
    Throwable thrown = null;
    try {
    task.run();
    } catch (RuntimeException x) {
    thrown = x; throw x;
    } catch (Error x) {
    thrown = x; throw x;
    } catch (Throwable x) {
    thrown = x; throw new Error(x);
    } finally {
    afterExecute(task, thrown);
    }
    } finally {
    task = null;
    w.completedTasks++;
    w.unlock();
    }
    }
    completedAbruptly = false;
    } finally {
    processWorkerExit(w, completedAbruptly);
    }
    }

7.getTask方法中是怎么获取任务队列中的任务的

  1. 判断线程池状态是否正常,根据timed = allowCoreThreadTimeout || wc > corePoolSize来决定队列获取任务的方式是指定keepAliveTime时间进行等待还是阻塞式等待

  2. 如果keepAliveTime超时,允许核心线程超时销毁或者当前线程池总量大于核心线程数,则getTask()返回null,回溯到runWorker方法中,则while循环结束,即线程执行完成,此线程将被销毁。

     private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?
    retry:
    for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);
    // Check if queue empty only if necessary.
    if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    decrementWorkerCount();
    return null;
    }
    boolean timed; // Are workers subject to culling?
    for (;;) {
    int wc = workerCountOf(c);
    timed = allowCoreThreadTimeOut || wc > corePoolSize;
    if (wc <= maximumPoolSize && ! (timedOut && timed))
    break;
    if (compareAndDecrementWorkerCount(c))
    return null;
    c = ctl.get(); // Re-read ctl
    if (runStateOf(c) != rs)
    continue retry;
    // else CAS failed due to workerCount change; retry inner loop
    }
    try {
    Runnable r = timed ?
    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
    workQueue.take();
    if (r != null)
    return r;
    timedOut = true;
    } catch (InterruptedException retry) {
    timedOut = false;
    }
    }
    }

ThreadPoolExecutor源码解析的更多相关文章

  1. ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...

  2. ThreadPoolExecutor系列三——ThreadPoolExecutor 源码解析

    ThreadPoolExecutor 源码解析 本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.htm ...

  3. 第十三章 ThreadPoolExecutor源码解析

    ThreadPoolExecutor使用方式.工作机理以及参数的详细介绍,请参照<第十二章 ThreadPoolExecutor使用与工作机理 > 1.源代码主要掌握两个部分 线程池的创建 ...

  4. Java并发包源码学习系列:线程池ThreadPoolExecutor源码解析

    目录 ThreadPoolExecutor概述 线程池解决的优点 线程池处理流程 创建线程池 重要常量及字段 线程池的五种状态及转换 ThreadPoolExecutor构造参数及参数意义 Work类 ...

  5. Java并发之ThreadPoolExecutor源码解析(二)

    ThreadPoolExecutor ThreadPoolExecutor是ExecutorService的一种实现,可以用若干已经池化的线程执行被提交的任务.使用线程池可以帮助我们限定和整合程序资源 ...

  6. 【Java并发编程】21、线程池ThreadPoolExecutor源码解析

    一.前言 JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了 ...

  7. Java 1.7 ThreadPoolExecutor源码解析

    Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ...

  8. ThreadPoolExecutor源码解析(二)

    1.ThreadPoolExcuter运行实例 首先我们先看如何新建一个ThreadPoolExecutor去运行线程.然后深入到源码中去看ThreadPoolExecutor里面使如何运作的. pu ...

  9. Java 并发系列(一) ThreadPoolExecutor源码解析及理解

    ThreadPoolExecutor 它是线程池最核心的类, 这里对核心的方法做简要的剖析(会持续更新),以加深对线程池运行原理的理解. 1. 核心成员变量及相关方法 // ctl非常重要,用整型表示 ...

随机推荐

  1. SpringBoot发布WAR启动报错:Error assembling WAR: webxml attribute is required

    Spring Boot发布war包流程: 1.修改web model的pom.xml <packaging>war</packaging> SpringBoot默认发布的都是j ...

  2. 在c#中using和new这两个关键字有什么意义?

    在c#中using和new这两个关键字有什么意义?答:using 引入名称空间或者使用非托管资源, new 新建实例或者隐藏基类方法

  3. CharSquence 接口的作用,多态以增强String

    CharSquence 接口的作用,多态以增强String,CharSquence 可以多态定义 String StringBuffer StringBuilder.

  4. MySQL另类的备份恢复方法——innodb可传输表空间

      Preface       There're many ways in backing up or migrating data from one server to another one.Lo ...

  5. 7-3 python操作excel

    1.写excel 写入特定单元格数据 # .导入xlwt模块 # .新建一个excel # .添加一个sheet页 # .往指定的单元格中写入数据 # .保存excel import xlwt boo ...

  6. Ubuntu 18.04 配置

    Ubuntu 18.04 配置IP-静态(UB与其他linux os不同) sudo netplan generate sudo vim /etc/netplan/50-cloud-init.yaml ...

  7. Codeforces Round #392 (Div. 2) Unfair Poll

    C. Unfair Poll time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  8. 机器学习实战 -- 决策树(ID3)

    机器学习实战 -- 决策树(ID3)   ID3是什么我也不知道,不急,知道他是干什么的就行   ID3是最经典最基础的一种决策树算法,他会将每一个特征都设为决策节点,有时候,一个数据集中,某些特征属 ...

  9. ABAP 7.51 構文書き方変換について

    便利になったなぁと感じたコマンドのメモです.NetWeaver7.40 SP2から使えます. SAP NetWeaver AS for ABAP 7.51 Innovation Package – O ...

  10. TouTiao开源项目 分析笔记7 加载数据的过程

    1.以新闻页中的段子数据显示为例 1.1.首先执行InitApp==>SplashActivity. 因为在AndroidManifest.xml中定义了一个<intent-filter& ...