线程池(一)线程池执行任务

234 阅读5分钟

线程池简介

线程池主要解决两个问题:(1)当执行大量异步任务时线程池能够提供较好的性能,线程池中的线程是可以复用的,不需要每次执行异步任务时都重新创建和销毁线程;(2)线程池提供了一种资源限制和管理的手段

线程池数据结构

线程池配置

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) corePoolSize: 核心线程数,在线程池中始终维护的线程个数

(2) maximumPoolSize: 线程池最大线程数

(3) keepAliveTime: 存活时间,当线程池中的线程数量比核心线程数量多,并且是闲置状态,则这些闲置的线程能够存活的最大时间

(4) unit: 存活时间的单位

(5) workQueue: 用于保存等待执行的任务的阻塞队列

(6) threadFactory: 创建线程的工厂

(7) handler: 拒绝策略,当队列满且线程个数达到maximumPoolSize后采取的策略

线程池状态

JDK将线程数量(workerCount)和线程池状态(runState)两个变量存储在ctl这一个字段中,其中最高三位存储线程池状态,其余29位存储线程个数。


// 初始,线程池状态位RUNNING,线程数位0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 最高三位表示线程池状态
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 线程池的5种状态
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;

// 从ctl中分别得到runState和workCount
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 将runState和workCount合并到Ctrl中
private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池的状态只会从小的状态值往大的状态值迁移,不会逆向迁移,线程池的5个状态迁移过程如下:

image.png

线程池运行任务

任务提交

execute()方法的作用是提交任务到线程池中执行,执行过程如下:

image.png

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 1. 当前线程池中线程个数小于corePoolSize则开启新线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2 .如果线程池处于Running状态,且能添加任务到阻塞队列
    if (isRunning(c) && workQueue.offer(command)) {
        // 2.1 二次检查
        int recheck = ctl.get();
        // 2.2 如果当前线程池不是Running状态,删除任务并执行拒绝策略
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 2.3 如果当前线程池为空,则增加一个线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3. 如果队列满,则新增线程,新增失败则执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

addWorker()方法用于新增线程,并启动线程任务

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 1. 返回false的三种情况
        //(1)当前线程池为STOP,TIDYING,TERNIBTED
        //(2)当前线程池为SHUTDOWN,当前任务非NULL
        //(3)当前线程池为SHUTDOWN,且任务队列为空
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
            return false;

        // 2. 循环CAS增加线程个数
        for (;;) {
            int wc = workerCountOf(c);
            // 2.1 线程个数超过限制返回false
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 2.2 CAS增加线程个数
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();
            if (runStateOf(c) != rs)
                continue retry;
        }
    }

    // 3. 到这里就是创建线程池线程成功
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 3.1 创建Worker
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 3.2 加独占锁,实现worker的同步
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int rs = runStateOf(ctl.get());

                // 3.3 重新检查线程池状态
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) 
                        throw new IllegalThreadStateException();
                    // 3.4 添加任务
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 3.5 添加任务成功后,启动线程池线程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

任务执行

Work类是线程池中的线程包装类,它实现了Runnable接口并实现了run()方法,Work类也实现了AQS,使用线程内部锁实现任务执行和线程中断的同步。

// 线程池中线程
final Thread thread;
// 初始化Work时的第一个任务
Runnable firstTask;
// 该线程完成的数目
volatile long completedTasks;

Worker(Runnable firstTask) {
    setState(-1); // 在调用runWork前禁止中断
    this.firstTask = firstTask; // 第一个任务
    this.thread = getThreadFactory().newThread(this); // 创建一个线程
}

// 线程执行
// 在addWork()方法中调用t.start()执行该方法,使用work的内部thread执行work内部的run()方法
public void run() {
    runWorker(this);
}

在构造方法内线设置Worker的状态为-1,这是为了避免当前Worker在调用runWorker()方法前被中断(当其他线程调用线程池的shutdown时,如果Worker状态>=0则会中断线程),这里设置为-1,所以线程不会被中断。在runWork()中,会先执行unlock()方法,该方法则是将state设置为0,所以这时调用shutdownNow会中断线程。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 1. 将state设置为0,允许中断
    w.unlock();
    boolean completedAbruptly = true;
    try {
        // 2. 若task==null且getTask() == null时,退出循环,即结束线程
        while (task != null || (task = getTask()) != null) {
            // 3. 获取工作线程内部持有的独占锁,保证任务执行期间,线程不被中断
            w.lock();
            // 4. 如果线程池被shutdownNow,则会直接中断当前线程
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 5. 执行用户提交的任务
                    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 {
                // 6. 完成的任务置null,增加线程完成的任务数量,并解锁
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 7. 线程退出后,回收线程
        processWorkerExit(w, completedAbruptly);
    }
}

在任务执行期间加锁,是为了避免在任务执行期间,其他线程调用了shutdown后正在执行的任务被中断(shutdown只会中断当前被阻塞挂起的线程)

获取任务

Work对象初始化时绑定一个任务,待绑定的任务执行完后,

private Runnable getTask() {
    boolean timedOut = false;

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

        // 1. 判断线程池状态,确认是否需要退出
        //(1)当线程池状态>=STOP时,返回null,减少线程数
        //(2)当线程池处于SHUTDOWN,且任务队列为空,返回null,减少线程数
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // 2. 断是否允许超时
        //(1)非核心线程数默认允许超时
        //(2)核心线程数是否允许超时由allowCoreThreadTimeOut参数决定
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 3. 当线程数大于1,或者工作队列为空时,下面两种情况会返回null 
        //(1)当线程数大于最大线程数时,返回null,减少线程数
        //(2)当允许超时且超时时,返回null,减少线程数
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 4. 在任务队列中获取任务,允许超时时,要超时阻塞等待线程,且可以被中断,
            Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            // 5. 当线程shutdown或者shutdownNow时,会中断等待的线程
            // 中断的线程再次进入for循环,再判断线程池状态时可以选择退出执行或继续执行
            timedOut = false;
        }
    }
}

线程退出

线程获取不到任务时,会执行退出逻辑,在退出逻辑中都会尝试关闭线程池,并且如果线程关闭后的数量低于预期(核心数或者当任务队列不为空,至少保留一个线程),会再重新创建线程。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly)
        decrementWorkerCount();

    // 1. 统计整个线程池完成的任务个数,并从线程池中删除该线程包装类Work
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    // 2. 尝试设置线程池状态为TERMINNATED,下面的情况会将线程池社会中为TERMINNATED
    // (1)当前是SHUDOWN状态,且工作队列为空
    // (2)当前是STOP状态,当前线程池中没有活动线程
    tryTerminate();

    // 3. 如果当前线程池个数小于核心线程数,则增加
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            // 3.1 如果核心线程池允许回收,则最小线程数位0,否则最小线程数位corePoolSize
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 3.2 任务不为空,要保留一个线程
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return;
        }
        // 3.3 线程数小于min时,再创建线程
        addWorker(null, false);
    }
}