线程池简介

线程池的优点:提高性能,提高线程的利用率,可控制线程的最大并发数,方便线程管理。
线程池的核心类是 ThreadPoolExecutor, 可通过 Executors 中的几个方法创建线程池,分别是:

  1. CachedThreadPool
    核心线程数为 0, 线程数量无上限
  2. FixedThreadPool
    核心线程数由使用者自己指定,无非核心线程,线程空闲时不会被回收,除非线程池被关闭
  3. ScheduledThreadPool
    指定数量的核心线程数,线程数无上限,非核心线程空闲时会被回收
  4. SingleThreadExecutor
    核心线程为 1, 无非核心线程,线程空闲时不会被回收。意义在于使得任务都放在同一线程处理,不必考虑线程同步问题。
线程池构造方法参数介绍
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
}

上面是线程池的构造方法,分析一下创建线程池的七个参数的含义:

  1. corePoolSize
    核心线程数,在空闲时不会被回收,在线程池关闭后才会被回收(allowCoreThreadTimeOut 被设为 true 时会闲置 keepAliveTime 后回收)。
  2. maximunPoolSize
    线程数上限,可利用该参数控制线程最大并发数,当运行的线程超过这个数量时会被阻塞。
  3. keepAliveTime
    非核心线程的闲置时长,超过这个时长就会被回收,当 allowCoreThreadTimeOut 被设为 true 后,这个时长也同样作用于核心线程。
  4. unit
    keepAliveTime 的时间单位。
  5. workQueue
    线程池中的任务队列。
  6. threadFactory
    用于创建线程,它只有一个 newThread(Runnable r) 方法。
  7. handler
    如果执行(添加)任务失败,则会执行 handler.rejectedExecution(command, this); 这个方法在需要处理拒绝后的操作时由我们自己实现来处理。
线程池执行过程

线程池创建过后执行会调用 ThreadPoolExecutor 的 execute(Runnable) 方法来执行线程,步骤如下:

  1. 如果当前线程数小于核心线程,则创建新的线程保存到核心线程并执行;
  2. 否则,核心线程数已满,则插入到任务队列中等待
  3. 插入到任务队列失败,则启动非核心线程来执行
  4. 启动非核心线程失败,则 reject.

源码解析

那么线程池传过去的 Runnable 是怎么运行的,任务队列是怎么排队的,核心线程又是怎么创建和销毁的,从源码中找答案。

Runnabla 是怎么运行的

首先调用 execute 方法,

public void execute(Runnable command) {
    
    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);
    }
    // 添加到非核心线程,失败则 reject
    else if (!addWorker(command, false))
        reject(command);
}

接下来看 addWorker 方法,执行 Runnable 的方法

private boolean addWorker(Runnable firstTask, boolean core) {
    // ... 一些校验条件

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
    	// 创建 Worker, 内部赋值了 firstTask 并创建了线程,通过 threadFactory.new Thread(worker)
        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)) {
                    // ...
                    // 把创建的 worker 对象保存下来
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 成功添加,则开始执行,t.start 会调用 worker.run 方法,因为 newThread 时传的 Runnable 对象是 worker
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

上面分析到 t.start 调的是 worker.run, worker.run 实际执行的是 runWorker(), 下面接着来看这个方法

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask; // 这里的 task 就是 execute 方法传的 Runnable 对象
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 循环取 task 执行,第一次 task 不是 null, 第一次循环执行完,下一次就会取 getTask
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // ... 校验
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 这里就是我们调用 execute 方法时实际执行的地方了
                    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);
    }
}

上面就看到 Runnable 实际执行的部分了,Runnable 怎么执行的分析就到此结束。

任务队列是怎么排队的呢

上面代码中看到一个 for 循环不断 getTask 就是获取 Runnable 的过程,下面看 getTask 方法

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

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

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            	// 取任务就是在这里,从 workQueue 中取任务,是通过 workQueue.offer(runnable) 添加的
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
核心线程池又是怎么创建和销毁的

创建核心线程就是在 new Worker 时创建的线程, runWorker方法中 while 循环 getTask 获取 Runnable, getTask 获取不到 Runnable 时会调用 processWorkerExit 来销毁线程,getTask 获取 Runnalble 是通过 workQueue.poll() / workQueue.take().

当创建线程池时设置空闲一定时间就销毁时,就调用 workQueue.poll(), 下面看一下 poll 方法的源码:

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    E x = null;
    int c = -1;
    // timeout 就是线程闲置这个时间后就需要销毁的
    long nanos = unit.toNanos(timeout);
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            if (nanos <= 0)
                return null;
            // 如果队列为空,线程就等待 timeout 时间
            nanos = notEmpty.awaitNanos(nanos);
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    // 所以如果这里在等待 timeout 时间后还没有任务,返回到的就是空
    return x;
}

上面 poll 方法中返回为空时,上面 runWorker 方法中的 while 循环就结束了,那么久会执行 processWorkerExit 方法来中断这个线程,于是就结束执行了这个线程。

设置 allowCoreThreadTimeOut 为 false 时就调用 workQueue.take(), workQueue.take() 方法看一下代码:

public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        // 当队列为空时,线程会一直处于等待状态
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    return x;
}

所以如果核心线程是不可销毁的,那么会调用 workQueue.take(), 在没有任务时线程就会处于等待状态,直到有任务再返回,所以这样情况下核心线程是不会被销毁的。