044_java.util.concurrent.ThreadPoolExecutor

简单说明

创建一个线程的成本还是很高的,一般为了节省创建线程的成本都会考虑进行池化,线程池就是为解决线程资源重用问题的,这里我们先看看怎么用线程池:


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorTest {

    public static void main(String[] args) {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5,
                10,
                200,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue(5)
        );


        for (int i = 0; i < 15; i++) {

            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
                    executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());

        }
        executor.shutdown();
    }

}

class MyTask implements Runnable {
    private int taskNum;
    public MyTask(int num) {
        this.taskNum = num;
    }

    @Override
    public void run() {
        System.out.println("正在执行task " + taskNum);
        
        try {
            Thread.currentThread().sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("task " + taskNum + "执行完毕");
    }
}

继承体系

image.png
线程池的继承是线性的,还是比较简单的,但是每个接口都挺重要,接下来我们一个一个看

Executor

java.util.concurrent.Executor 是任务的执行者接口,它是线程池框架的基础,表达其子类具备执行一个任务的能力。代码如下:

public interface Executor {
    void execute(Runnable command);
}

ExcutorService

ExcutorService 扩展了 Executor,添加了操控线程池生命周期的方法,如 shutDown(),shutDownNow()等,以及扩展了可异步跟踪执行任务生成返回值 Future 的方法,如submit()等方法。这个接口存在的意义是表达其子类具备将任务提交给执行者的能力,并赋予执行者执行的生命周期的概念。代码如下:

public interface ExecutorService extends Executor {

    /**
     * 启动一次顺序关闭,执行以前提交的任务,但不接受新任务
     */
    void shutdown();

    /**
     * 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表
     */
    List<Runnable> shutdownNow();

    /**
     * 如果此执行程序已关闭,则返回 true。
     */
    boolean isShutdown();

    /**
     * 如果关闭后所有任务都已完成,则返回 true
     */
    boolean isTerminated();

    /**
     * 请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行
     */
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

    /**
     * 提交一个返回值的任务用于执行,返回一个Future
     */
    <T> Future<T> submit(Callable<T> task);

    /**
     * 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
     */
    <T> Future<T> submit(Runnable task, T result);

    /**
     * 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
     */
    Future<?> submit(Runnable task);

    /**
     * 执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;

    /**
     * 执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, 
                                  TimeUnit unit) throws InterruptedException;

    /**
     * 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;

    /**
     * 执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, 
                    TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

AbstractExecutorService

java.util.concurrent.AbstractExecutorService 是一个抽象类,实现 ExecutorService 接口,为其提供默认实现。AbstractExecutorService 除了实现 ExecutorService 接口外,还提供了 #newTaskFor(...) 方法,返回一个 RunnableFuture 对象,在运行的时候,它将调用底层可调用任务,作为 Future 任务,它将生成可调用的结果作为其结果,并为底层任务提供取消操作。代码如下:

public abstract class AbstractExecutorService implements ExecutorService {

    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
    ...
}

重要字段

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;

// 处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。
private static final int RUNNING    = -1 << COUNT_BITS;
// 调用 shutdown() 方法会使线程池进入到该状态,该状态下不再继续接受新提交的任务,但是还会处理阻塞队列中的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 调用 shutdownNow() 方法会使线程池进入到该状态,该状态下不再继续接受新提交的任务,同时不再处理阻塞队列中的任务
private static final int STOP       =  1 << COUNT_BITS;
// 如果线程池中workerCount=0,即有效线程数量为0时,会进入该状态
private static final int TIDYING    =  2 << COUNT_BITS;
// 线程池彻底终止的状态
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

线程有五种状态:新建,就绪,运行,阻塞,死亡,线程池同样有五种状态:Running, SHUTDOWN, STOP, TIDYING, TERMINATED。

上述变量中,ctl字段比较重要,其类型定义为AtomicInteger ,记录了"线程池中的任务数量"和"线程池的状态"两个信息。共32位的bit,其中高3位表示"线程池状态",低29位表示"线程池中的任务数量"。设计者认为在多线程的环境下,运行状态和有效线程数量往往需要保证统一,不能出现一个改而另一个没有改的情况。意味着需要让两个信息的修改是原子化的。如果将他们放在同一个 AtomicInteger中,利用 AtomicInteger 的原子操作,就可以保证这两个值始终是统一的。用锁当然也可以,但是总会产生阻塞其他线程的情况。

COUNT_BITS 表示ctl变量中表示有效线程数量的位数,这里COUNT_BITS=29,使用COUNT_BITS计算的CAPACITY表示最大有效线程数,数量是2^29-1。同样使用COUNT_BITS操作ctl可以获得线程池的状态,会在后续详细说明。

构造函数

线程池最原始的构造器如下:

ThreadPoolExecutor(int corePoolSize,
                   int maximumPoolSize, 
                   long keepAliveTime, 
                   TimeUnit unit, 
                   BlockingQueue<Runnable> workQueue, 
                   ThreadFactory threadFactory, 
                   RejectedExecutionHandler handler)

线程池的构造函数的参数相当多,我们每一项都描述一遍。

第一项参数:corePoolSize(核心线程数)

当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,才会根据是否存在空闲线程,来决定是否需要创建新的线程。除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。

第二项参数:maximumPoolSize(线程池线程数最大值)

线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。

第三项参数:keepAliveTime(线程存活保持时间):

默认情况下,当线程池的线程个数多于corePoolSize时,线程的空闲时间超过keepAliveTime则会终止。但只要keepAliveTime大于0,allowCoreThreadTimeOut(boolean) 方法也可将此超时策略应用于核心线程。另外,也可以使用setKeepAliveTime()动态地更改参数。

第四项参数:unit(存活时间的单位):

时间单位,分为7类,从细到粗顺序:NANOSECONDS(纳秒),MICROSECONDS(微妙),MILLISECONDS(毫秒),SECONDS(秒),MINUTES(分),HOURS(小时),DAYS(天);

第五项参数:workQueue(任务队列):

用于传输和保存等待执行任务的阻塞队列。可以使用此队列与线程池进行交互。阻塞队列的选择可以是:ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue等等

第六项参数:threadFactory(线程工厂):

threadFactory用于创建新线程。由同一个threadFactory创建的线程,属于同一个ThreadGroup,创建的线程优先级都为Thread.NORM_PRIORITY,以及是非守护进程状态。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。我们也可以自定义一个线程工厂,通过实现 ThreadFactory 接口来完成,这样就可以自定义线程的名称或线程执行的优先级了。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    // // Executors.defaultThreadFactory() 为默认的线程创建工厂
    this(corePoolSize, 
         maximumPoolSize, 
         keepAliveTime, 
         unit, 
         workQueue, 
         Executors.defaultThreadFactory(), 
         defaultHandler);
}

public static ThreadFactory defaultThreadFactory() {
    return new DefaultThreadFactory();
}

// 默认的线程创建工厂,需要实现 ThreadFactory 接口
static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
    }
    // 创建线程
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
        if (t.isDaemon()) 
            // 创建一个非守护线程
            t.setDaemon(false); 
        if (t.getPriority() != Thread.NORM_PRIORITY)
            // 线程优先级设置为默认值
            t.setPriority(Thread.NORM_PRIORITY); 
        return t;
    }
}

第七项参数:handler(线程饱和策略)

当线程池和队列都满了,则表明该线程池已达饱和状态。

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    1,
    3,
    10,
    TimeUnit.SECONDS, new LinkedBlockingQueue<>(2),
    new ThreadPoolExecutor.AbortPolicy() // 添加 AbortPolicy 拒绝策略
); 

for (int i = 0; i < 6; i++) {
    executor.execute(() -> {
        System.out.println(Thread.currentThread().getName());
    });
}

获得到的输出是:

pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.lagou.interview.ThreadPoolExample$$Lambda$1/1096979270@448139f0 rejected from java.util.concurrent.ThreadPoolExecutor@7cca494b[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]
 at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
 at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
 at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
 at com.lagou.interview.ThreadPoolExample.rejected(ThreadPoolExample.java:35)
 at com.lagou.interview.ThreadPoolExample.main(ThreadPoolExample.java:26)

可以看出当第 6 个任务来的时候,线程池则执行了 AbortPolicy  拒绝策略,抛出了异常。因为队列最多存储 2 个任务,最大可以创建 3 个线程来执行任务(2+3=5),所以当第 6 个任务来的时候,此线程池就忙不过来了,直接抛出异常。当然除了上面提到的ThreadPoolExecutor.AbortPolicy的饱和策略,还有几个饱和策略:
1)ThreadPoolExecutor.AbortPolicy
处理程序遭到拒绝,则直接抛出运行时异常 RejectedExecutionException。(默认策略)
2)ThreadPoolExecutor.CallerRunsPolicy
调用者所在线程来运行该任务,此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
3)ThreadPoolExecutor.DiscardPolicy
无法执行的任务将被删除。
4)ThreadPoolExecutor.DiscardOldestPolicy
如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重新尝试执行任务(如果再次失败,则重复此过程)。

如果我们发现JDK自带的拒绝策略还是不能满足与自己的需求,我们就可以自己造一个出来,想实现自定义拒绝策略只需要新建一个 RejectedExecutionHandler 对象,然后重写它的 rejectedExecution() 方法即可,如下代码所示:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
				1, 
				3, 
				10,
        TimeUnit.SECONDS, 
        new LinkedBlockingQueue<>(2),
        new RejectedExecutionHandler() {  // 添加自定义拒绝策略
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("开始执行自定义拒绝策略");
            }
        });
for (int i = 0; i < 6; i++) {
    executor.execute(() -> {
        System.out.println(Thread.currentThread().getName());
    });
}

工作原理

线程池的整个生命周期异常复杂,其内部控制的点很多。还是之前的方式,我们把握主线,其他所有的细节我们放在源码解析的部分详细分析。线程池的主线无非是不断往里面添加上任务,那么我们就以这个切入点来描述当你提交一个新的任务的时候会发生的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按照上面的流程图描述的那样,当提交任务后,线程池首先会检查当前线程数,如果此时线程数小于核心线程数,比如最开始线程数量为 0,则新建线程并执行任务,随着任务的不断增加,线程数会逐渐增加并达到核心线程数,此时如果仍有任务被不断提交,就会被放入 workQueue 任务队列中,等待核心线程执行完当前任务后重新从 workQueue 中提取正在等待被执行的任务。此时,假设我们的任务特别的多,已经达到了 workQueue 的容量上限,这时线程池就会启动后备力量,也就是 maxPoolSize 最大线程数,线程池会在 corePoolSize 核心线程数的基础上继续创建线程来执行任务,假设任务被不断提交,线程池会持续创建线程直到线程数达到 maxPoolSize 最大线程数,如果依然有任务被提交,这就超过了线程池的最大处理能力,这个时候线程池就会拒绝这些任务,我们可以看到实际上任务进来之后,线程池会逐一判断 corePoolSize 、workQueue 、maxPoolSize ,如果依然不能满足需求,则会拒绝任务。

重要方法

线程池状态控制方法

首先我们先搞定线程池本身的一些状态问题,上文中我们提到了ctl,其中高三位的值表达了线程池的状态,总共有以下几种:
1) RUNNING:高三位值111 ,表示可接受新任务,且可执行队列中的任务
2) SHUTDOWN:高三位值000 ,表示不接受新任务,但可执行队列中的任务
3) STOP:高三位值001 ,表示不接受新任务,且不再执行队列中的任务,且中断正在执行的任务
4) TIDYING:高三位值010 所有任务已经中止,且工作线程数量为0,最后变迁到这个状态的线程将要执行terminated()钩子方法,只会有一个线程执行这个方法
5) TERMINATED:高三位值011 ,中止状态,已经执行完terminated()钩子方法
其对应的状态扭转图如下:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
线程池对象内有相应的判断方法来判断当前线城池处于什么状态,如下:

public boolean isShutdown() {
    return ! isRunning(ctl.get());
}
public boolean isTerminating() {
    int c = ctl.get();
    return ! isRunning(c) && runStateLessThan(c, TERMINATED);
}
private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

状态的初始值是RUNNING,很好理解,创建线程池的时候就会初始化ctl,而ctl初始化为RUNNING状态,所以线程池的初始状态就为RUNNING状态。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

shutdown方法

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        // 推进线程状态
        advanceRunState(SHUTDOWN);
        // 中断空闲的线程
        interruptIdleWorkers();
        // 交给子类实现
        onShutdown();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

private void advanceRunState(int targetState) {
    for (;;) {
        int c = ctl.get();
        // 如果状态大于SHUTDOWN,或者修改为SHUTDOWN成功了,才会break跳出自旋
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

执行shutdown()方法时把状态修改为SHUTDOWN,这里肯定会成功,因为advanceRunState()方法中是个自旋,不成功不会退出。

shutdownNow方法

执行shutdownNow()方法时,会把线程池状态修改为STOP状态,同时标记所有线程为中断状态,并返回所有等待执行的任务列表。

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);
        // 中断所有线程
        interruptWorkers();
        // 返回等待执行的任务列表
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

与shutdown不同,shutdownNow会调用interruptWorkers()方法中断所有线程,他会在线程上挂上中断标记,真正响应会在队列的take()或poll()方法中响应的,最后会到AQS中,它们检测到线程中断了会抛出一个InterruptedException异常,然后getTask()中捕获这个异常,并且在下一次的自旋时退出当前线程并减少工作线程的数量。

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}

同时会调用drainQueue()方法返回等待执行到任务列表。

private List<Runnable> drainQueue() {
    BlockingQueue<Runnable> q = workQueue;
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    q.drainTo(taskList);
    if (!q.isEmpty()) {
        for (Runnable r : q.toArray(new Runnable[0])) {
            if (q.remove(r))
                taskList.add(r);
        }
    }
    return taskList;
}

tryTerminate方法

当执行shutdown()或shutdownNow()之后,如果所有任务已中止,且工作线程数量为0,就会进入TIDYING状态。tryTerminated()方法在上述两个方法中或者线程退出时,所以说几乎每个线程最后消亡的时候都会调用tryTerminate()方法,但最后只会有一个线程真正执行到修改状态为TIDYING的地方。修改状态为TIDYING后执行terminated()方法,最后修改状态为TERMINATED,标志着线程池真正消亡了。

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 1. 运行中
        // 2. 状态的值比TIDYING还大,也就是TERMINATED
        // 3. SHUTDOWN状态且任务队列不为空
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        // 工作线程数量不为0,也不会执行后续代码
        if (workerCountOf(c) != 0) {
            // 尝试中断空闲的线程
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // CAS修改状态为TIDYING状态
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    // 更新成功,执行terminated钩子方法
                    terminated();
                } finally {
                    // 强制更新状态为TERMINATED,这里不需要CAS了
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

线程池提交普通任务方法

execute方法

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

这段代码短小精悍,内容丰富。接下来我们具体分析代码中的逻辑,顺序按照上面源码的标记号来。

a)execute 方法中通过 if 语句判断 command ,也就是  Runnable 任务是否等于 null,如果为 null 就抛出异常。

b)判断当前线程数是否小于核心线程数,如果小于核心线程数就调用 addWorker() 方法增加一个 Worker,这里的 Worker 就可以理解为一个不断运行的线程。其中addWorker 方法的主要作用是在线程池中创建一个线程并执行第一个参数传入的任务,它的第二个参数是个布尔值,如果布尔值传入 true 代表增加线程时判断当前线程是否少于 corePoolSize,小于则增加新线程,大于等于则不增加;同理,如果传入 false 代表增加线程时判断当前线程是否少于 maxPoolSize,小于则增加新线程,大于等于则不增加,所以这里的布尔值的含义是以核心线程数为界限还是以最大线程数为界限进行是否新增线程的判断。addWorker() 方法如果返回 true 代表添加成功,如果返回 false 代表添加失败。

c)如果代码执行到这里,说明当前线程数大于或等于核心线程数或者 addWorker 失败了,那么就需要通过 if (isRunning© && workQueue.offer(command)) 检查线程池状态是否为 Running,如果线程池状态是 Running 就把任务放入任务队列中,也就是 workQueue.offer(command)。如果线程池已经不处于 Running 状态,说明线程池被关闭,那么就移除刚刚添加到任务队列中的任务,并执行拒绝策略。

d)能进入这个 else 说明前面判断到线程池状态为 Running,那么当任务被添加进来之后就需要防止没有可执行线程的情况发生(比如之前的线程被回收了或意外终止了),所以此时如果检查当前线程数为 0,也就是 workerCountOf**(**recheck) == 0,那就执行 addWorker() 方法新建线程。

e)执行到这里,说明线程池不是 Running 状态或线程数大于或等于核心线程数并且任务队列已经满了,根据规则,此时需要添加新线程,直到线程数达到“最大线程数”,所以此时就会再次调用 addWorker 方法并将第二个参数传入 false,传入 false 代表增加线程时判断当前线程数是否少于 maxPoolSize,小于则增加新线程,大于等于则不增加,也就是以 maxPoolSize 为上限创建新的 worker;addWorker 方法如果返回 true 代表添加成功,如果返回 false 代表任务添加失败,说明当前线程数已经达到 maxPoolSize,然后执行拒绝策略 reject 方法。如果执行到这里线程池的状态不是 Running,那么 addWorker 会失败并返回 false,所以也会执行拒绝策略 reject 方法。

Worker内部类

上面聊了任务流转的过程的描述了,真正执行还得看下addWorker,这里也得先理解Worker是什么,简单理解Worker内部类可以看作是对工作线程的包装,一般地,我们说工作线程就是指Worker,但实际上是指其维护的Thread实例。我们把这个类的定义拿出来:

// Worker继承自AQS,自带锁的属性
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    // 真正工作的线程
    final Thread thread;
    // 第一个任务,从构造方法传进来
    Runnable firstTask;
    // 完成任务数
    volatile long completedTasks;

    // 构造函数
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 使用线程工厂生成一个线程
        // 注意,这里把Worker本身作为Runnable传给线程
        this.thread = getThreadFactory().newThread(this);
    }

    // 实现Runnable的run()方法
    public void run() {
        // 调用ThreadPoolExecutor的runWorker()方法
        runWorker(this);
    }
    
    // 省略锁的部分
}

能够看出来工作线程Thread启动的时候实际是调用的Worker的run()方法,进而调用的是ThreadPoolExecutor的runWorker()方法。

runWorker

final void runWorker(Worker w) {
    // 工作线程
    Thread wt = Thread.currentThread();
    // 任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 强制释放锁(shutdown()里面有加锁)
    // 这里相当于无视那边的中断标记
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 取任务,如果有第一个任务,这里先执行第一个任务
        // 只要能取到任务,这就是个死循环
        // 正常来说getTask()返回的任务是不可能为空的,因为前面execute()方法是有空判断的
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 检查线程池的状态
            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置为空,重新从队列中取
                task = null;
                // 完成任务数加1
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 到这里肯定是上面的while循环退出了
        processWorkerExit(w, completedAbruptly);
    }
}

private void processWorkerExit(Worker w, boolean completedAbruptly) {

    // true:用户线程运行异常,需要扣减
    // false:getTask方法中扣减线程数量
    if (completedAbruptly)
        decrementWorkerCount();

    // 获取主锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        // 从HashSet中移出worker
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    // 有worker线程移除,可能是最后一个线程退出需要尝试终止线程池
    tryTerminate();

    int c = ctl.get();
    // 如果线程为running或shutdown状态,即tryTerminate()没有成功终止线程池,则判断是否有必要一个worker
    if (runStateLessThan(c, STOP)) {
        // 正常退出,计算min:需要维护的最小线程数量
        if (!completedAbruptly) {
            // allowCoreThreadTimeOut 默认false:是否需要维持核心线程的数量
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 如果min ==0 或者workerQueue为空,min = 1
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;

            // 如果线程数量大于最少数量min,直接返回,不需要新增线程
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 添加一个没有firstTask的worker
        addWorker(null, false);
    }

忽略状态检测和锁的内容,如果有第一个任务,就先执行之,之后再从任务队列中取任务来执行,获取任务是通过getTask()来进行的。

getTask

private Runnable getTask() {
    // 是否超时
    boolean timedOut = false;
    
    // 死循环
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 线程池状态是SHUTDOWN的时候会把队列中的任务执行完直到队列为空
        // 线程池状态是STOP时立即退出
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        
        // 工作线程数量// 【本文由公从号“彤哥读源码”原创】
        int wc = workerCountOf(c);

        // 是否允许超时,有两种情况:
        // 1. 是允许核心线程数超时,这种就是说所有的线程都可能超时
        // 2. 是工作线程数大于了核心数量,这种肯定是允许超时的
        // 注意,非核心线程是一定允许超时的,这里的超时其实是指取任务超时
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 超时判断(还包含一些容错判断)
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            // 超时了,减少工作线程数量,并返回null
            if (compareAndDecrementWorkerCount(c))
                return null;
            // 减少工作线程数量失败,则重试
            continue;
        }

        try {
            // 真正取任务的地方
            // 默认情况下,只有当工作线程数量大于核心线程数量时,才会调用poll()方法触发超时调用
            
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            
            // 取到任务了就正常返回
            if (r != null)
                return r;
            // 没取到任务表明超时了,回到continue那个if中返回null
            timedOut = true;
        } catch (InterruptedException retry) {
            // 捕获到了中断异常
            // 中断标记是在调用shutDown()或者shutDownNow()的时候设置进去的
            // 此时,会回到for循环的第一个if处判断状态是否要返回null
            timedOut = false;
        }
    }
}

注意,这里取任务会根据工作线程的数量判断是使用BlockingQueue的poll(timeout, unit)方法还是take()方法。
poll(timeout, unit)方法会在超时时返回null,如果timeout<=0,队列为空时直接返回null。
take()方法会一直阻塞直到取到任务或抛出中断异常。
所以,如果keepAliveTime设置为0,当任务队列为空时,非核心线程取不出来任务,会立即结束其生命周期。
默认情况下,是不允许核心线程超时的,但是可以通过下面这个方法设置使核心线程也可超时。

public void allowCoreThreadTimeOut(boolean value) {
    if (value && keepAliveTime <= 0)
        throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
    if (value != allowCoreThreadTimeOut) {
        allowCoreThreadTimeOut = value;
        if (value)
            interruptIdleWorkers();
    }
}

addWorkers

上面了解了worker的执行,这里回到最初始的代码,看看worker是怎么创建的,方式是调用了addworker

private boolean addWorker(Runnable firstTask, boolean core) {
    // 判断有没有资格创建新的工作线程
    // 主要是一些状态/数量的检查等等
    // 这段代码比较复杂,可以先跳过
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 线程池状态检查
        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;
            // 数量加1并跳出循环
            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
        }
    }
    
    // 如果上面的条件满足,则会把工作线程数量加1,然后执行下面创建线程的动作

    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();
            try {
                // 再次检查线程池的状态
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    
                    // 添加到工作线程队列
                    workers.add(w);
                    // 还在池子中的线程数量(只能在mainLock中使用)
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    
                    // 标记线程添加成功
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 线程添加成功之后启动线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 线程启动失败,执行失败方法(线程数量减1,执行tryTerminate()方法等)
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

线程池提交未来任务方法

线程池中还有一种任务,叫作FutureTask,,使用它您可以获取任务执行的结果。future体系如下:
image.png
FutureTask实现了RunnableFuture接口,而RunnableFuture接口组合了Runnable接口和Future接口的能力,而Future接口提供了get任务返回值的能力。Future,RunnableFuture接口定义如下:

public interface Future<V> {

    /**
     * 试图取消对此任务的执行
     * 如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。
     * 当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。
     * 如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程
     */
    boolean cancel(boolean mayInterruptIfRunning);

    /**
     * 如果在任务正常完成前将其取消,则返回 true
     */
    boolean isCancelled();

    /**
     * 如果任务已完成,则返回 true
     */
    boolean isDone();

    /**
     *   如有必要,等待计算完成,然后获取其结果
     */
    V get() throws InterruptedException, ExecutionException;

    /**
     * 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)
     */
    V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;
}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    //在未被取消的情况下,将此 Future 设置为计算的结果
    void run();
}

submit()

AbstractExecutorService 提供了 #newTaskFor(...) 方法,返回一个RunnableFuture 对象。除此之外,当我们把一个 Runnable 或者 Callable 提交给( submit 方法)ThreadPoolExecutor 或者 ScheduledThreadPoolExecutor 时,他们则会向我们返回一个FutureTask对象。如下:


protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    // 将普通任务包装成FutureTask
    return new FutureTask<T>(callable);
}

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

public <T> Future<T> submit(Callable<T> task) {
    // 非空检测
    if (task == null) throw new NullPointerException();
    // 包装成FutureTask
    RunnableFuture<T> ftask = newTaskFor(task);
    // 交给execute()方法去执行
    execute(ftask);
    // 返回futureTask
    return ftask;
}

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

FutureTask#run

execute()方法最后调用的是task的run()方法,上面我们传进去的任务,最后被包装成了FutureTask,也就是说execute()方法最后会调用到FutureTask的run()方法,所以我们直接看这个方法就可以了。

public void run() {
    // 状态不为NEW,或者修改为当前线程来运行这个任务失败,则直接返回
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    
    try {
        // 真正的任务
        Callable<V> c = callable;
        // state必须为NEW时才运行
        if (c != null && state == NEW) {
            // 运行的结果
            V result;
            boolean ran;
            try {
                // 任务执行的地方
                result = c.call();
                // 已执行完毕
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                // 处理异常
                setException(ex);
            }
            if (ran)
                // 处理结果
                set(result);
        }
    } finally {
        // 置空runner
        runner = null;
        // 处理中断
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

protected void setException(Throwable t) {
    // 将状态从NEW置为COMPLETING
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        // 返回值置为传进来的异常(outcome为调用get()方法时返回的)
        outcome = t;
        // 最终的状态设置为EXCEPTIONAL
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        // 调用完成方法
        finishCompletion();
    }
}
protected void set(V v) {
    // 将状态从NEW置为COMPLETING
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        // 返回值置为传进来的结果(outcome为调用get()方法时返回的)
        outcome = v;
        // 最终的状态设置为NORMAL
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        // 调用完成方法
        finishCompletion();
    }
}

private void finishCompletion() {
    // 如果队列不为空(这个队列实际上为调用者线程)
    for (WaitNode q; (q = waiters) != null;) {
        // 置空队列
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                // 调用者线程
                Thread t = q.thread;
                if (t != null) {
                    q.thread = null;
                    // 如果调用者线程不为空,则唤醒它
                    LockSupport.unpark(t);
                }
                WaitNode next = q.next;
                if (next == null)
                    break;
                q.next = null; // unlink to help gc
                q = next;
            }
            break;
        }
    }
    // 钩子方法,子类重写
    done();
    // 置空任务
    callable = null;        // to reduce footprint
}

FutureTask#get

调用get()方法,会将调用者线程到FutureTask中:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // 如果状态小于等于COMPLETING,则进入队列等待
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    // 返回结果(异常)
    return report(s);
}

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // 如果状态小于等于COMPLETING,则进入队列等待
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    // 返回结果(异常)
    return report(s);
}

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    // 我们这里假设不带超时
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        // 处理中断
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }
        // 4. 如果状态大于COMPLETING了,则跳出循环并返回
        // 这是自旋的出口
        int s = state;
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        // 如果状态等于COMPLETING,说明任务快完成了,就差设置状态到NORMAL或EXCEPTIONAL和设置结果了
        // 这时候就让出CPU,优先完成任务
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        // 1. 如果队列为空
        else if (q == null)
            // 初始化队列(WaitNode中记录了调用者线程)
            q = new WaitNode();
        // 2. 未进入队列
        else if (!queued)
            // 尝试入队
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        // 超时处理
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        // 3. 阻塞当前线程(调用者线程)
        else
            LockSupport.park(this);
    }
}

private V report(int s) throws ExecutionException {
    Object x = outcome;
    // 任务正常结束
    if (s == NORMAL)
        return (V)x;
    // 被取消了
    if (s >= CANCELLED)
        throw new CancellationException();
    // 执行异常
    throw new ExecutionException((Throwable)x);
}

这里我们假设调用get()时任务还未执行,也就是其状态为NEW,我们试着按上面标示的1、2、3、4走一遍逻辑:
(1)第一次循环,状态为NEW,直接到1处,初始化队列并把调用者线程封装在WaitNode中;
(2)第二次循环,状态为NEW,队列不为空,到2处,让包含调用者线程的WaitNode入队;
(3)第三次循环,状态为NEW,队列不为空,且已入队,到3处,阻塞调用者线程;
(4)假设过了一会任务执行完毕了,根据run()方法的分析最后会unpark调用者线程,也就是3处会被唤醒;
(5)第四次循环,状态肯定大于COMPLETING了,退出循环并返回;

最后一步获得任务返回值,如果正常执行结束,则返回任务的返回值,如果异常结束,则包装成ExecutionException异常抛出。通过这种方式,线程中出现的异常也可以返回给调用者线程了,不会像执行普通任务那样调用者是不知道任务执行到底有没有成功的。

FutureTask#cancel

FutureTask除了可以获取任务的返回值以外,还能够取消任务的执行。

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

这里取消任务是通过中断执行线程来处理的

总结

线程池处理复杂,但是本质上先搞明白其行为流程就能把源码看地大差不大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值