Java ThreadPoolExecutor 线程池

一、创建线程池

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html

/**
 * @param corePoolSize 核心线程池大小
 * 当提交一个任务到线程池时,如果当前 poolSize < corePoolSize 时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程
 * 等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的 prestartAllCoreThreads() 方法,线程池会提前创建并启动所有基本线程。
 *
 * @param maximumPoolSize 最大线程池大小
 * 线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
 * 值得注意的是,如果使用了无界的任务队列这个参数就没什么效果。
 *
 * @param keepAliveTime 线程活动保持时间
 * 线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
 *
 * @param unit 线程活动保持时间的单位
 * TimeUnit.DAYS:天
 * TimeUnit.HOURS:小时
 * TimeUnit.MINUTES:分钟
 * TimeUnit.MILLISECONDS:毫秒
 * TimeUnit.MICROSECONDS:微秒,千分之一毫秒
 * TimeUnit.NANOSECONDS:纳秒,千分之一微秒
 *
 * @param workQueue 保存等待执行的任务的阻塞队列
 * ArrayBlockingQueue:一个基于数组结构的有界阻塞队列,按FIFO(先进先出)进行排序
 * LinkedBlockingQueue:一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,吞吐量通常要高于ArrayBlockingQueue。
 * SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue。
 * PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
 *
 * @param threadFactory 创建线程的工厂
 * 使用开源框架 guava 提供的 ThreadFactoryBuilder 可以快速给线程池里的线程设置有意义的名字
 * ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
 *
 * @param handler maxmumPoolSize + workQueue 都满了之后处理新提交任务的策略
 * AbortPolicy:直接抛出异常(默认)。
 * CallerRunsPolicy:只用调用者所在线程来运行任务。
 * DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
 * DiscardPolicy:不处理,丢弃掉。
 * 也可实现 RejectedExecutionHandler 接口自定义策略,如记录日志或持久化存储不能处理的任务。
 */
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

 

二、使用线程池

2.1.提交无返回值任务 execute(),输入的任务是一个 Runnable 类的实例,无法判断任务是否被线程池执行成功

// 线程工厂,这里主要用来设置线程名字
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
// 创建线程池
ThreadPoolExecutor singleThreadPool = new ThreadPoolExecutor(
        1,
        1,
        0L,
        TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024),
        namedThreadFactory,
        new ThreadPoolExecutor.AbortPolicy());
// 向线程池提交任务
singleThreadPool.execute(() -> System.out.println(Thread.currentThread().getName()));

2.2.提交有返回值任务 submit(),输入的任务是一个 Callable 或 Runnable 类的实例,有返回值,且可抛出异常,可中断线程

// 线程工厂,这里主要用来设置线程名字
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
// 创建线程池
ThreadPoolExecutor singleThreadPool = new ThreadPoolExecutor(
        1,
        1,
        0L,
        TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024),
        namedThreadFactory,
        new ThreadPoolExecutor.AbortPolicy());
// 添加任务
Future<String> future = singleThreadPool.submit(new Callable<String>() {
    @Override
    public String call() {
        return Thread.currentThread().getName();
    }
});

// 获取结果,会阻塞当前线程,这里调用了有参方法,指定了阻塞时间,若在时间内未执行完则获取结果会报错
System.out.println("结果:" + future.get(1, TimeUnit.SECONDS));
System.out.println("是否执行完成:" + future.isDone());

2.3.关闭线程池 shutdown() 与 shutdownNow()

// 调用后,不可以再submit新的task,已经submit的将继续执行。会遍历已经在线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程
singleThreadPool.shutdown();
// 首先将线程池的状态设置成STOP,然后试图停止当前正执行或暂停的 task 的线程,并返回尚未(等待)执行的 task 的 list
List<Runnable> runnables = singleThreadPool.shutdownNow();

 

三、线程池处理流程

对应到代码中 ThreadPoolExecutor 的 execute() 方法

四、ThreadPoolExecutor 源码

属性变量

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 一个 int 类型的数字,高 3 位表示线程池状态,低 29 位表示 worker 数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // Integer.SIZE 为 32,所以 COUNT_BITS 为 29
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 线程池允许的最大线程数。1 左移 29 位,然后减 1,即为 2^29 - 1
    private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

    // 线程池有 5 种状态,按大小排序:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
    private static final int RUNNING = -1 << COUNT_BITS;
    private static final int SHUTDOWN = 0 << COUNT_BITS;
    private static final int STOP = 1 << COUNT_BITS;
    private static final int TIDYING = 2 << COUNT_BITS;
    private static final int TERMINATED = 3 << COUNT_BITS;


    private static int runStateOf(int c) { // 获取线程池状态,通过按位与操作,低 29 位将全部变成 0
        return c & ~COUNT_MASK;
    }

    private static int workerCountOf(int c) { // 获取线程池 worker 数量,通过按位与操作,高 3 位将全部变成 0
        return c & COUNT_MASK;
    }

    private static int ctlOf(int rs, int wc) { // 根据线程池状态和线程池 worker 数量,生成 ctl 值
        return rs | wc;
    }

    private static boolean runStateLessThan(int c, int s) { // 线程池状态小于 xx
        return c < s;
    }

    private static boolean runStateAtLeast(int c, int s) { // 线程池状态大于等于 xx
        return c >= s;
    }

    private static boolean isRunning(int c) { // 线程池状态小于 SHUTDOWN,判断是否在运行
        return c < SHUTDOWN;
    }

构造方法

public class ThreadPoolExecutor extends AbstractExecutorService {
    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, 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); // 根据传入参数 unit 和 keepAliveTime,将存活时间转换为纳秒存到变量 keepAliveTime 中
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

execute 执行任务方法,submit 也是调用的 execute

public abstract class AbstractExecutorService implements ExecutorService {
    public Future<?> submit(Runnable task) {
    public <T> Future<T> submit(Runnable task, T result) {
    public <T> Future<T> submit(Callable<T> task) {

public class ThreadPoolExecutor extends AbstractExecutorService {
    public void execute(Runnable command) {
        if (command == null) // 如果提交了空的任务则抛出异常
            throw new NullPointerException();

        // 分 3 步

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { // 1、worker 线程数量比核心线程数小,直接创建 worker 执行任务
            if (addWorker(command, true)) // 启动新线程(核心),对 addWorker 的调用以原子方式检查 runState 和 workerCount
                return;
            c = ctl.get(); // 如果提交失败 则二次检查状态
        }

        if (isRunning(c) && workQueue.offer(command)) { // 2、线程池处于运行状态,worker 线程数量超过核心线程数,任务直接进入阻塞队列
            int recheck = ctl.get();
            // 添加到队列成功,再检查一次线程池的状态,如果线程池关闭了,就将刚才添加的任务从队列中移除,并执行拒绝策略
            // 线程池状态不是 RUNNING 状态,说明执行过 shutdown 命令,需要对新加入的任务执行 reject() 操作
            // 这儿为什么需要 recheck,是因为任务入队列前后,线程池的状态可能会发生变化。
            if (!isRunning(recheck) && remove(command))
                reject(command);
            // 如果当前线程池线程空,则添加一个新线程,为什么需要判断 0 值,主要是在线程池构造方法中,核心线程数允许为 0
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }

        // 3、如果线程池不是运行状态,或者任务进入队列失败,则尝试创建 worker 执行任务。
        // 这有几点需要注意:
        // * 线程池不是运行状态时,addWorker 内部会判断线程池状态
        // * addWorker 第 2 个参数表示是否创建核心线程
        // * addWorker 返回 false,则说明任务执行失败,需要执行 reject 操作
        else if (!addWorker(command, false)) // 尝试添加一个新线程(非核心),新增失败(线程池已关闭或已饱和)则执行拒绝策略
            reject(command);
    }

addWorker 添加任务方法

public class ThreadPoolExecutor extends AbstractExecutorService {
    private boolean addWorker(Runnable firstTask, boolean core) {

        // 大致分为两部分

        // 1、增加线程池个数
        retry:
        for (int c = ctl.get(); ; ) { // 外层自旋
            // 这个条件写得比较难懂,对其进行了调整,和下面的条件等价
            // runStateAtLeast(c, SHUTDOWN) ---> 先决条件 true:线程池状态不是 RUNNING 时
            // runStateAtLeast(c, STOP) ---> 1. 线程池状态不是 RUNNING 时,且线程池状态不是 SHUTDOWN 时,返回 false
            // firstTask != null ---> 2. 线程池状态不是 RUNNING 时,且 firstTask 不为 null,直接返回 false
            // workQueue.isEmpty() ---> 3. 线程池状态不是 RUNNING 时,且队列为空,直接返回 false
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN) && (runStateAtLeast(c, STOP) || firstTask != null || workQueue.isEmpty()))
                return false;

            for (; ; ) { // 内层自旋
                if (workerCountOf(c) >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) // worker 数量超过容量,直接返回 false
                    return false;
                if (compareAndIncrementWorkerCount(c)) // 使用 CAS 的方式增加 worker 数量。若增加成功,则直接跳出外层循环进入到第二部分
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateAtLeast(c, SHUTDOWN)) // 线程池状态发生变化,对外层循环进行自旋
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
                // 其他情况,直接内层循环进行自旋即可
            }
        }

        // 2、将任务添加到 workers 里面并执行
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock(); // worker 的添加必须是串行的,因此需要加锁
                try {
                    // Recheck while holding lock. Back out on ThreadFactory failure or if shut down before lock acquired.
                    int c = ctl.get();

                    if (isRunning(c) || (runStateLessThan(c, STOP) && firstTask == null)) { // 这儿需要重新检查线程池状态
                        if (t.getState() != Thread.State.NEW) // worker 已经调用过了 start() 方法,则不再创建 worker
                            throw new IllegalThreadStateException();
                        workers.add(w);// worker 创建并添加到 workers 成功
                        workerAdded = true; // 设置新增标志
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s; // 更新 largestPoolSize 变量
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) { // 如果 worker 是新增的,就启动该线程
                    t.start(); // 启动 worker 线程
                    workerStarted = true; // 成功启动了线程,设置对应的标志
                }
            }
        } finally {
            if (!workerStarted) // 判断线程是否启动成功,若 worker 线程启动失败,说明线程池状态发生了变化(关闭操作被执行),需要进行 shutdown 相关操作
                addWorkerFailed(w);
        }
        return workerStarted;
    }

线程池 worker 任务单元 t.start() 实际调用的是 runWorker()

public class ThreadPoolExecutor extends AbstractExecutorService {
    private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
        private static final long serialVersionUID = 6138294804551838833L;
        final Thread thread;
        Runnable firstTask;
        volatile long completedTasks;

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this); // 这儿是 Worker 的关键所在,使用了线程工厂创建了一个线程。传入的参数为当前 worker
        }

        public void run() {
            runWorker(this);
        }
    }

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // 调用 unlock() 是为了允许其它线程来中断自己
        boolean completedAbruptly = true;
        try {
            // 这儿是自旋,循环获取任务
            // 1. 如果 firstTask 不为 null,则执行 firstTask;
            // 2. 如果 firstTask 为 null,则调用 getTask() 从阻塞队列获取任务。阻塞队列的特性就是:当队列为空时,当前线程会被阻塞等待
            while (task != null || (task = getTask()) != null) {
                // 这儿对 worker 进行加锁,是为了达到下面的目的
                // 1. 降低锁范围,提升性能
                // 2. 保证每个 worker 执行的任务是串行的
                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();
                // 执行任务,且在执行前后通过 beforeExecute() 和 afterExecute()来扩展其功能。这两个方法在当前类里面为空实现。
                try {
                    beforeExecute(wt, task);
                    try {
                        task.run(); // 执行任务
                        afterExecute(task, null);
                    } catch (Throwable ex) {
                        afterExecute(task, ex);
                        throw ex;
                    }
                } finally {
                    task = null; // 帮助 gc
                    w.completedTasks++; // 统计当前线程完成了多少个任务,已完成任务数加一
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly); // 自旋操作被退出,说明线程池正在结束
        }
    }

getTask() 获取任务

public class ThreadPoolExecutor extends AbstractExecutorService {
    private Runnable getTask() {
        // 最后一次 poll() 是否超时
        boolean timedOut = false; // Did the last poll() time out?

        for (; ; ) {
            int c = ctl.get();

            // Check if queue empty only if necessary.
            // 仅在必要时检查队列是否为空,如果线程池已经关闭了,就直接返回 null
            // SHUTDOWN 状态表示执行了 shutdown() 方法,STOP 表示执行了 shutdownNow() 方法
            if (runStateAtLeast(c, SHUTDOWN) && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c); // 线程数量

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 核心 worker 是否超时,当前正在运行的 worker 数量是否超过了 corePoolSize

            // 如果上一次循环从队列获取到的为 null,这时 timedOut 就会为 true 了
            if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
                // 通过 cas 来设置 WorkerCount,多个线程竞争,只有一个可以设置成功
                // 没设置成功,进入下一次循环,可能下次 worker 的数量就没有超过 corePoolSize,也就不用销毁 worker
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                // 获取任务,超过 keepAliveTime 时间还没有任务进队列就会返回 null,worker 会销毁
                Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

 


https://www.jianshu.com/p/098819be088c

https://blog.csdn.net/u013332124/article/details/79587436

http://www.cnblogs.com/fixzd/p/9253203.html

https://blog.csdn.net/f641385712/article/details/80832636

https://blog.csdn.net/f641385712/article/details/83714243

posted @ 2019-05-13 11:12  江湖小小白  阅读(655)  评论(0编辑  收藏  举报