Java线程池ThreadPoolExecutor源码解析(一)

JDK提供了线程池的简单创建方式,通过Executors提供的API可以创建出不同类型的线程池,例如

// 创建一个单线程的线程池;
		ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            System.out.println("TEST EXECUTOR");
        });

一、这里需要引入几个问题

  1. 构成线程池的要素?
  2. 线程池中的线程如何工作?

二、线程池的构造

2.1、线程池包装

当点进Executors.newSingleThreadExecutor()中可以看到下面代码

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

上面代码为构建一个ThreadPoolExecutor, 同时将构建好的ThreadPoolExecutor作为参数传递进FinalizableDelegatedExecutorService对象中,这里应用到了装饰者模式,仅仅通过FinalizableDelegatedExecutorService对ThreadPoolExecutor进行一层包装,代码如下

static class FinalizableDelegatedExecutorService
        extends DelegatedExecutorService {
        FinalizableDelegatedExecutorService(ExecutorService executor) {
            super(executor);
        }
        protected void finalize() {
            super.shutdown();
        }
    }

实际上FinalizableDelegatedExecutorService仅仅只是将ThreadPoolExecutor传递给DelegatedExecutorService,除此之外仅仅只是重写Object.finalize方法去调用ExecutorService.shutdown,而DelegatedExecutorService只是对ThreadPoolExecutor进行包装,目的只是为暴露出ExecutorService中的方法(exposes only the ExecutorService methods),当执行ExecutorService的方法时,实际上执行的是ThreadPoolExecutor的方法,代码如下

/**
     * A wrapper class that exposes only the ExecutorService methods
     * of an ExecutorService implementation.
     */
	static class DelegatedExecutorService extends AbstractExecutorService {
        private final ExecutorService e;
        DelegatedExecutorService(ExecutorService executor) { e = executor; }
        public void execute(Runnable command) { e.execute(command); }
        public void shutdown() { e.shutdown(); }
        // 省略N多代码.....
    }

OK,讲到这里,实际上我们已经明确,当我们通过Executors.newSingleThreadExecutor构建出来的ExecutorService,实际上是去构建ThreadPoolExecutor,那么下面将分析下ThreadPoolExecutor是如何构建的,以及个构造参数的含义

2.2、ThreadPoolExecutor构造参数

从Executors.newSingleThreadExecutor一直往下跟,可以看到ThreadPoolExecutor构造方法如下

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

这里涉及的几个参数含义如下

  1. corePoolSize:核心线程的数量,线程池中的线程分为核心线程和非核心线程
  2. maximumPoolSize:线程池最大的线程数量,线程池不能无节制的创建线程,这里起到一个限制作用
  3. keepAliveTime:空闲线程保持时间值,与TimeUnit结合使用
  4. TimeUnit:时间单位,与keepAliveTime结合使用
  5. BlockingQueue:任务队列,一般线程池执行任务时,如果任务数大于线程数,会将任务放入队列进行等待
  6. ThreadFactory:生产线程的工厂,线程池是通过ThreadFactory构建线程并工作的
  7. RejectedExecutionHandler:拒绝策略,当线程池无法执行任务时,会走对应的拒绝策略,JDK默认提供了4种,分别为(AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy),具体作用后续细说

2.3、ThreadPoolExecutor中的任务队列(BlockingQueue)

同样在Executors.newSingleThreadExecutor代码中可以看出,ThreadPoolExecutor中使用的是LinkedBlockingQueue作为任务队列,这是基于AQS机制实现的一种线程安全的容器,内部采用单向链表的存储结构,遵循FIFO,后续讲解其实现机制,这里简单说明下在实际使用中会应用到的方法

  1. BlockingQueue.offer:向队列中存放元素,线程池内部放入的是任务(Runnable)
  2. BlockingQueue.take:向队列获取元素,如果队列没有元素,会阻塞线程,直至队列存在元素
  3. BlockingQueue.poll:同take作用,但poll设置了超时机制,如果规定时间内无法从队列中获取元素,则返回NULL

以上是LinkedBlockingQueueBlockingQueue中的offer、take和poll方法的实现,由于内容较多,后续针对LinkedBlockingQueue的源码进行单独解析

2.4、ThreadPoolExecutor中的线程工厂(ThreadFactory)

在ThreadPoolExecutor构造方法中,如果没有指明对应的ThreadFactory,默认情况下会用过Executors.defaultThreadFactory构建一个简单的线程工厂(DefaultThreadFactory),其代码如下

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;
        }
    }

newThread中为构建好的线程附上一个名字,内部依旧是通过new Thread的方式去创建线程
线程池的构造到这里就差不多结束了,还有部分细节尚未提到,建议有兴趣的朋友可以去跟源代码,走一遍线程池的构造

三、线程池的工作原理

3.1、submit和execute区别

当需要通过线程池来执行任务时,通常可以选择调用submit和execute,二者区别在于submit可以传入Callable或者Runnable,而execute中传入的是Runnable,实际上submit内部只是对Callable或者Runnable进行封装,最终还是调用了execute,代码如下

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(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

可以看出,无论submit传入的是Callable或者Runnable,最终都会通过newTaskFor封装为一个FutureTask,调用execute并返回给调用者,而调用者一旦执行了FutureTask.get则会等待结果的返回

3.2、execute方法详解

通常线程池在通过execute执行任务时会经过下列几个判断

  1. 判断线程池当前线程数是否小于核心线程数,如果小于,则开启核心线程去运行任务
  2. 在不满足条件1的情况下,尝试将任务放入队列中,如果放入成功还会进行重校验,逻辑如下
    2.1. 当任务放入成功后会再次校验线程池的状态,如果线程池非RUNNING状态,即放入任务期间,线程池关闭了,则会尝试将任务从队列移除,并走拒绝策略
    2.2. 如果2.1判断未通过,且重校验的线程数等于0,则开启一个非核心线程,去任务队列中获取并执行任务
  3. 如果条件1和2都不满足的情况下,即不小于核心线程数,队列也无法容纳新元素,此时会尝试开启一个非核心线程去执行任务,如果执行失败,则通过reject走预先设定的拒绝策略;
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
  		int c = ctl.get();
  		// 通过workerCountOf获取当前线程数,判断当前线程数是否小于核心线程数,如果小于核心线程数,则调用addWorker去执行任务;
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果线程池当前线程数已经不小于核心线程数,就会尝试把任务放入队列中,放入成功的与否取决于offer是否执行成功,即队列仍有空间容纳元素;
        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);
        }
        // 如果上述两个判断都无法通过,即不小于核心线程数,队列也无法容纳新元素,此时会尝试开启一个非核心线程去执行任务,如果执行失败,则通过reject走预先设定的拒绝策略;
        else if (!addWorker(command, false))
            reject(command);
}

3.3、addWorker方法

从execute中可以看出,线程池会通过addWorker方法去执行任务,addWorker方法会对任务进行封装,将Runnable封装为一个Worker,同时将Worker放入线程集合workers中,如果放入成功,则执行Thread.start开启线程执行任务,实际上执行的是Worker的run方法,部分关键代码如下

private final HashSet<Worker> workers = new HashSet<Worker>();

private boolean addWorker(Runnable firstTask, boolean core) {
	// 此处省略部分代码.....
	 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 {
                 // Recheck while holding lock.
                 // Back out on ThreadFactory failure or if
                 // shut down before lock acquired.
                 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);
                     int s = workers.size();
                     if (s > largestPoolSize)
                         largestPoolSize = s;
                     workerAdded = true;
                 }
             } finally {
                 mainLock.unlock();
             }
             if (workerAdded) {
                 t.start();
                 workerStarted = true;
             }
         }
     } finally {
         if (! workerStarted)
             addWorkerFailed(w);
     }
     return workerStarted;
}

Worker结构如下

  1. 通过ThreadFactory获取Thread,并赋值给thread属性
  2. 实现Runnable重写run方法,调用内部runWorker
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
    	// 省略部分代码......
        final Thread thread;
        Runnable firstTask;
        volatile long completedTasks;
        
        Worker(Runnable firstTask) {
            setState(-1); 
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        
        public void run() {
            runWorker(this);
        }
    }

3.4 runWorker让线程反复执行任务

runWorker执行任务原理如下

  1. runWorker方法内部会开启一个while循环,不断地通过getTask方法去任务队列中获取任务
  2. 如果队列中依旧存在任务,执行beforeExecute方法,此方法是扩展方法,默认无任何处理
  3. 执行task.run,task对象是一开始放入队列中的Runnable
  4. 在finally中执行afterExecute,默认同beforeExecute一样
  5. 为Worker的completedTasks计数,记录Worker执行过的任务数
  6. 当任务队列清空后,执行processWorkerExit,清除线程
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock();
        boolean completedAbruptly = true;
        try {
            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 = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }