java 线程池原理

ThreadPoolExecutor简介

ThreadPoolExecutor是线程池类。对于线程池,可以通俗的将它理解为”存放一定数量线程的一个线程集合。线程池允许若个线程同时允许,允许同时运行的线程数量就是线程池的容量;当添加的到线程池中的线程超过它的容量时,会有一部分线程阻塞等待。线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理。”

ThreadPoolExecutor数据结构

ThreadPoolExecutor的数据结构如下图所示:

各个数据在ThreadPoolExecutor.java中的定义如下:

// 阻塞队列。
private final BlockingQueue<Runnable> workQueue;
// 互斥锁
private final ReentrantLock mainLock = new ReentrantLock();
// 线程集合。一个Worker对应一个线程。
private final HashSet<Worker> workers = new HashSet<Worker>();
// “终止条件”,与“mainLock”绑定。
private final Condition termination = mainLock.newCondition();
// 线程池中线程数量曾经达到过的最大值。
private int largestPoolSize;
// 已完成任务数量
private long completedTaskCount;
// ThreadFactory对象,用于创建线程。
private volatile ThreadFactory threadFactory;
// 拒绝策略的处理句柄。
private volatile RejectedExecutionHandler handler;
// 保持线程存活时间。
private volatile long keepAliveTime;

private volatile boolean allowCoreThreadTimeOut;
// 核心池大小
private volatile int corePoolSize;
// 最大池大小
private volatile int maximumPoolSize;

1. workers

  workers是HashSet类型,即它是一个Worker集合。而一个Worker对应一个线程,也就是说线程池通过workers包含了”一个线程集合”。当Worker对应的线程池启动时,它会执行线程池中的任务;当执行完一个任务后,它会从线程池的阻塞队列中取出一个阻塞的任务来继续运行。
wokers的作用是,线程池通过它实现了”允许多个线程同时运行”。

2. workQueue

  workQueue是BlockingQueue类型,即它是一个阻塞队列。当线程池中的线程数超过它的容量的时候,线程会进入阻塞队列进行阻塞等待。
  通过workQueue,线程池实现了阻塞功能。

3. mainLock

  mainLock是互斥锁,通过mainLock实现了对线程池的互斥访问。

4. corePoolSize和maximumPoolSize

  corePoolSize是”核心池大小”,maximumPoolSize是”最大池大小”。它们的作用是调整”线程池中实际运行的线程的数量”。
  例如,当新任务提交给线程池时(通过execute方法)。
     – 如果此时,线程池中运行的线程数量< corePoolSize,则创建新线程来处理请求。
    – 如果此时,线程池中运行的线程数量> corePoolSize,但是却< maximumPoolSize;则仅当阻塞队列满时才创建新线程。
如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心池大小和最大池大小的值是在创建线程池设置的;但是,也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。

5. poolSize

  poolSize是当前线程池的实际大小,即线程池中任务的数量。

6. allowCoreThreadTimeOut和keepAliveTime

  allowCoreThreadTimeOut表示是否允许”线程在空闲状态时,仍然能够存活”;而keepAliveTime是当线程池处于空闲状态的时候,超过keepAliveTime时间之后,空闲的线程会被终止。

7. threadFactory

  threadFactory是ThreadFactory对象。它是一个线程工厂类,”线程池通过ThreadFactory创建线程”。

8. handler

  handler是RejectedExecutionHandler类型。它是”线程池拒绝策略”的句柄,也就是说”当某任务添加到线程池中,而线程池拒绝该任务时,线程池会通过handler进行相应的处理”。

9. Worker

private final class Worker extends AbstractQueuedSynchronizer implements Runnable ...

内部类 Worker 是对任务的封装,所有submit的Runnable都被封装成了Worker,它本身也是一个Runnable, 然后利用AQS框架实现了一个简单的非重入的互斥锁, 实现互斥锁主要目的是为了中断的时候判断线程是在空闲还是运行,可以看后面 shutdown 和 shutdownNow 方法的分析。

// state只有0和1,互斥
protected boolean tryAcquire(int unused) {
  if (compareAndSetState(0, 1)) {
    setExclusiveOwnerThread(Thread.currentThread());
    return true;// 成功获得锁
  }
  // 线程进入等待队列
  return false;
}
protected boolean tryRelease(int unused) {
  setExclusiveOwnerThread(null);
  setState(0);
  return true;
}

之所以不用ReentrantLock是为了避免任务执行的代码中修改线程池的变量,如 setCorePoolSize ,因为ReentrantLock是可重入的。

综上所说,线程池通过workers来管理”线程集合”,每个线程在启动后,会执行线程池中的任务;当一个任务执行完后,它会从线程池的阻塞队列中取出任务来继续运行。阻塞队列是管理线程池任务的队列,当添加到线程池中的任务超过线程池的容量时,该任务就会进入阻塞队列进行等待。

线程池调度

我们通过下面的图看看下面线程池中任务的调度策略,加深对线程池的理解。

图-01:

图-02:

说明
  在”图-01”中,线程池中有N个任务。”任务1”, “任务2”, “任务3”这3个任务在执行,而”任务3”到”任务N”在阻塞队列中等待。正在执行的任务,在workers集合中,workers集合包含3个Worker,每一个Worker对应一个Thread线程,Thread线程每次处理一个任务。
  当workers集合中处理完某一个任务之后,会从阻塞队列中取出一个任务来继续执行,如图-02所示。图-02表示”任务1”处理完毕之后,线程池将”任务4”从阻塞队列中取出,放到workers中进行处理。

线程池示例

在分析线程池之前,先看一个简单的线程池示例。

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class ThreadPoolDemo1 {

    public static void main(String[] args) {
        // 创建一个可重用固定线程数的线程池
        ExecutorService pool = Executors.newFixedThreadPool(2);
        // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
        Thread ta = new MyThread();
        Thread tb = new MyThread();
        Thread tc = new MyThread();
        Thread td = new MyThread();
        Thread te = new MyThread();
        // 将线程放入池中进行执行
        pool.execute(ta);
        pool.execute(tb);
        pool.execute(tc);
        pool.execute(td);
        pool.execute(te);
        // 关闭线程池
        pool.shutdown();
    }
}

class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+ " is running.");
    }
}

运行结果

pool-1-thread-1 is running.
pool-1-thread-2 is running.
pool-1-thread-1 is running.
pool-1-thread-2 is running.
pool-1-thread-1 is running.

示例中,包括了线程池的创建,将任务添加到线程池中,关闭线程池这3个主要的步骤。稍后,我们会从这3个方面来分析ThreadPoolExecutor。

线程池源码分析

(一) 创建“线程池”

下面以newFixedThreadPool()介绍线程池的创建过程。

1. newFixedThreadPool()

newFixedThreadPool()在Executors.java中定义,源码如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

说明:newFixedThreadPool(int nThreads)的作用是创建一个线程池,线程池的容量是nThreads。
   newFixedThreadPool()在调用ThreadPoolExecutor()时,会传递一个LinkedBlockingQueue()对象,而LinkedBlockingQueue是单向链表实现的阻塞队列。在线程池中,就是通过该阻塞队列来实现”当线程池中任务数量超过允许的任务数量时,部分任务会阻塞等待”。
关于LinkedBlockingQueue的实现细节,读者可以参考”Java多线程系列–“JUC集合”08之 LinkedBlockingQueue”。

2. ThreadPoolExecutor()

ThreadPoolExecutor()在ThreadPoolExecutor.java中定义,源码如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

说明:该函数实际上是调用ThreadPoolExecutor的另外一个构造函数。该函数的源码如下:

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);
    // 线程工厂对象
    this.threadFactory = threadFactory;
    // 拒绝策略的句柄
    this.handler = handler;
}

说明:在ThreadPoolExecutor()的构造函数中,进行的是初始化工作。
corePoolSize, maximumPoolSize, unit, keepAliveTime和workQueue这些变量的值是已知的,它们都是通过newFixedThreadPool()传递而来。下面看看threadFactory和handler对象。

2.1 ThreadFactory

线程池中的ThreadFactory是一个线程工厂,线程池创建线程都是通过线程工厂对象(threadFactory)来完成的。
上面所说的threadFactory对象,是通过 Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源码如下:

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

defaultThreadFactory()返回DefaultThreadFactory对象。Executors.java中的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-";
    }

    // 提供创建线程的API。
    public Thread newThread(Runnable r) {
        // 线程对应的任务是Runnable对象r
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        // 设为“非守护线程”
        if (t.isDaemon())
            t.setDaemon(false);
        // 将优先级设为“Thread.NORM_PRIORITY”
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

说明:ThreadFactory的作用就是提供创建线程的功能的线程工厂。
   它是通过newThread()提供创建线程功能的,下面简单说说newThread()。newThread()创建的线程对应的任务是Runnable对象,它创建的线程都是“非守护线程”而且“线程优先级都是Thread.NORM_PRIORITY”。

2.2 RejectedExecutionHandler

handler是ThreadPoolExecutor中拒绝策略的处理句柄。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。
线程池默认会采用的是defaultHandler策略,即AbortPolicy策略。在AbortPolicy策略中,线程池拒绝任务时会抛出异常!
defaultHandler的定义如下:

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

AbortPolicy的源码如下:

public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    // 抛出异常
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}

(二) 添加任务到“线程池”

1. execute()
execute 方法主要三个步骤:

  • 活动线程小于corePoolSize的时候创建新的线程;
  • 活动线程大于corePoolSize时都是先加入到任务队列当中;
  • 任务队列满了再去启动新的线程,如果线程数达到最大值就拒绝任务。

execute()定义在ThreadPoolExecutor.java中,源码如下:

public void execute(Runnable command) {
    // 如果任务为null,则抛出异常。
    if (command == null)
        throw new NullPointerException();
    // 获取ctl对应的int值。该int值保存了"线程池中任务的数量"和"线程池状态"信息
    int c = ctl.get();
    // 当线程池中的任务数量 < "核心池大小"时,即线程池中少于corePoolSize个任务。
    // 则通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 当线程池中的任务数量 >= "核心池大小"时,
    // 而且,"线程池处于允许状态"时,则尝试将任务添加到阻塞队列中。
    if (isRunning(c) && workQueue.offer(command)) {
        // 再次确认“线程池状态”,若线程池异常终止了,则删除任务;然后通过reject()执行相应的拒绝策略的内容。
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 否则,如果"线程池中任务数量"为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
    // 如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。
    else if (!addWorker(command, false))
        reject(command);
}

说明:execute()的作用是将任务添加到线程池中执行。它会分为3种情况进行处理:
   情况1 – 如果”线程池中任务数量” < “核心池大小”时,即线程池中少于corePoolSize个任务;此时就新建一个线程,并将该任务添加到线程中进行执行。
   情况2 – 如果”线程池中任务数量” >= “核心池大小”,并且”线程池是允许状态”;此时,则将任务添加到阻塞队列中阻塞等待。在该情况下,会再次确认”线程池的状态”,如果”第2次读到的线程池状态”和”第1次读到的线程池状态”不同,则从阻塞队列中删除该任务。
   情况3 – 非以上两种情况。在这种情况下,尝试新建一个线程,并将该任务添加到线程中进行执行。如果执行失败,则通过reject()拒绝该任务。

注释比较清楚了就不再解释了,其中比较难理解的应该是 addWorker(null, false); 这一行,这要结合addWorker一起来看。 主要目的是防止HUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。

2. addWorker()

addWorker()的源码如下:

private boolean addWorker(Runnable firstTask, boolean core) {
    retry: for (;;) {
      int c = ctl.get();
      int rs = runStateOf(c);// 当前线程池状态

      // Check if queue empty only if necessary.
      // 这条语句等价:rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null ||
      // workQueue.isEmpty())
      // 满足下列调价则直接返回false,线程创建失败:
      // rs > SHUTDOWN:STOP || TIDYING || TERMINATED 此时不再接受新的任务,且所有任务执行结束
      // rs = SHUTDOWN:firtTask != null 此时不再接受任务,但是仍然会执行队列中的任务
      // rs = SHUTDOWN:firtTask == null见execute方法的addWorker(null,
      // false),任务为null && 队列为空
      // 最后一种情况也就是说SHUTDONW状态下,如果队列不为空还得接着往下执行,为什么?add一个null任务目的到底是什么?
      // 看execute方法只有workCount==0的时候firstTask才会为null结合这里的条件就是线程池SHUTDOWN了不再接受新任务
      // 但是此时队列不为空,那么还得创建线程把任务给执行完才行。
      if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
        return false;

      // 走到这的情形:
      // 1.线程池状态为RUNNING
      // 2.SHUTDOWN状态,但队列中还有任务需要执行
      for (;;) {
        int wc = workerCountOf(c);
        if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
          return false;
        if (compareAndIncrementWorkerCount(c))// 原子操作递增workCount
          break retry;// 操作成功跳出的重试的循环
        c = ctl.get(); // Re-read ctl
        if (runStateOf(c) != rs)// 如果线程池的状态发生变化则重试
          continue retry;
        // else CAS failed due to workerCount change; retry inner loop
      }
    }

    // wokerCount递增成功

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
      final ReentrantLock mainLock = this.mainLock;
      w = new Worker(firstTask);
      final Thread t = w.thread;
      if (t != null) {
        // 并发的访问线程池workers对象必须加锁
        mainLock.lock();
        try {
          // Recheck while holding lock.
          // Back out on ThreadFactory failure or if
          // shut down before lock acquired.
          int c = ctl.get();
          int rs = runStateOf(c);

          // RUNNING状态 || SHUTDONW状态下清理队列中剩余的任务
          if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
            if (t.isAlive()) // precheck that t is startable
              throw new IllegalThreadStateException();
            // 将新启动的线程添加到线程池中
            workers.add(w);
            // 更新largestPoolSize
            int s = workers.size();
            if (s > largestPoolSize)
              largestPoolSize = s;
            workerAdded = true;
          }
        } finally {
          mainLock.unlock();
        }
        // 启动新添加的线程,这个线程首先执行firstTask,然后不停的从队列中取任务执行
        // 当等待keepAlieTime还没有任务执行则该线程结束。见runWoker和getTask方法的代码。
        if (workerAdded) {
          t.start();// 最终执行的是ThreadPoolExecutor的runWoker方法
          workerStarted = true;
        }
      }
    } finally {
      // 线程启动失败,则从wokers中移除w并递减wokerCount
      if (!workerStarted)
        // 递减wokerCount会触发tryTerminate方法
        addWorkerFailed(w);
    }
    return workerStarted;
  }

说明
  addWorker(Runnable firstTask, boolean core) 的作用是将任务(firstTask)添加到线程池中,并启动该任务。
  core为true的话,则以corePoolSize为界限,若”线程池中已有任务数量>=corePoolSize”,则返回false;core为false的话,则以maximumPoolSize为界限,若”线程池中已有任务数量>=maximumPoolSize”,则返回false。
  addWorker()会先通过for循环不断尝试更新ctl状态,ctl记录了”线程池中任务数量和线程池状态”。
更新成功之后,再通过try模块来将任务添加到线程池中,并启动任务所在的线程。

  从addWorker()中,我们能清晰的发现:线程池在添加任务时,会创建任务对应的Worker对象;而一个Workder对象包含一个Thread对象。(01) 通过将Worker对象添加到”线程的workers集合”中,从而实现将任务添加到线程池中。 (02) 通过启动Worker对应的Thread线程,则执行该任务。

3. runWorker
任务添加成功后实际执行的是 runWorker 这个方法,这个方法非常重要,简单来说它做的就是:

  • 第一次启动会执行初始化传进来的任务firstTask;
  • 然后会从workQueue中取任务执行,如果队列为空则等待keepAliveTime这么长时间。
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // Worker的构造函数中抑制了线程中断setState(-1),所以这里需要unlock从而允许中断
    w.unlock();
    // 用于标识是否异常终止,finally中processWorkerExit的方法会有不同逻辑
    // 为true的情况:1.执行任务抛出异常;2.被中断。
    boolean completedAbruptly = true;
    try {
      // 如果getTask返回null那么getTask中会将workerCount递减,如果异常了这个递减操作会在processWorkerExit中处理
      while (task != null || (task = getTask()) != null) {
        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();
        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 {
            // 和beforeExecute一样,留给子类去重载
            afterExecute(task, thrown);
          }
        } finally {
          task = null;
          w.completedTasks++;
          w.unlock();
        }
      }

      completedAbruptly = false;
    } finally {
      // 结束线程的一些清理工作
      processWorkerExit(w, completedAbruptly);
    }
  }

4. getTask

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

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

      // Check if queue empty only if necessary.
      // 1.rs > SHUTDOWN 所以rs至少等于STOP,这时不再处理队列中的任务
      // 2.rs = SHUTDOWN 所以rs>=STOP肯定不成立,这时还需要处理队列中的任务除非队列为空
      // 这两种情况都会返回null让runWoker退出while循环也就是当前线程结束了,所以必须要decrement
      // wokerCount
      if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
        // 递减workerCount值
        decrementWorkerCount();
        return null;
      }

      // 标记从队列中取任务时是否设置超时时间
      boolean timed; // Are workers subject to culling?

      // 1.RUNING状态
      // 2.SHUTDOWN状态,但队列中还有任务需要执行
      for (;;) {
        int wc = workerCountOf(c);

        // 1.core thread允许被超时,那么超过corePoolSize的的线程必定有超时
        // 2.allowCoreThreadTimeOut == false && wc >
        // corePoolSize时,一般都是这种情况,core thread即使空闲也不会被回收,只要超过的线程才会
        timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 从addWorker可以看到一般wc不会大于maximumPoolSize,所以更关心后面半句的情形:
        // 1. timedOut == false 第一次执行循环, 从队列中取出任务不为null方法返回 或者
        // poll出异常了重试
        // 2.timeOut == true && timed ==
        // false:看后面的代码workerQueue.poll超时时timeOut才为true,
        // 并且timed要为false,这两个条件相悖不可能同时成立(既然有超时那么timed肯定为true)
        // 所以超时不会继续执行而是return null结束线程。(重点:线程是如何超时的???)
        if (wc <= maximumPoolSize && !(timedOut && timed))
          break;

        // workerCount递减,结束当前thread
        if (compareAndDecrementWorkerCount(c))
          return null;
        c = ctl.get(); // Re-read ctl
        // 需要重新检查线程池状态,因为上述操作过程中线程池可能被SHUTDOWN
        if (runStateOf(c) != rs)
          continue retry;
        // else CAS failed due to workerCount change; retry inner loop
      }

      try {
        // 1.以指定的超时时间从队列中取任务
        // 2.core thread没有超时
        Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
        if (r != null)
          return r;
        timedOut = true;// 超时
      } catch (InterruptedException retry) {
        timedOut = false;// 线程被中断重试
      }
    }
  }

5. processWorkerExit
线程退出会执行这个方法做一些清理工作。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 正常的话再runWorker的getTask方法workerCount已经被减一了
    if (completedAbruptly)
      decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
      // 累加线程的completedTasks
      completedTaskCount += w.completedTasks;
      // 从线程池中移除超时或者出现异常的线程
      workers.remove(w);
    } finally {
      mainLock.unlock();
    }

    // 尝试停止线程池
    tryTerminate();

    int c = ctl.get();
    // runState为RUNNING或SHUTDOWN
    if (runStateLessThan(c, STOP)) {
      // 线程不是异常结束
      if (!completedAbruptly) {
        // 线程池最小空闲数,允许core thread超时就是0,否则就是corePoolSize
        int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
        // 如果min == 0但是队列不为空要保证有1个线程来执行队列中的任务
        if (min == 0 && !workQueue.isEmpty())
          min = 1;
        // 线程池还不为空那就不用担心了
        if (workerCountOf(c) >= min)
          return; // replacement not needed
      }
      // 1.线程异常退出
      // 2.线程池为空,但是队列中还有任务没执行,看addWoker方法对这种情况的处理
      addWorker(null, false);
    }
  }

6. tryTerminate
processWorkerExit方法中会尝试调用tryTerminate来终止线程池。这个方法在任何可能导致线程池终止的动作后执行:比如减少wokerCount或SHUTDOWN状态下从队列中移除任务。

final void tryTerminate() {
    for (;;) {
      int c = ctl.get();
      // 以下状态直接返回:
      // 1.线程池还处于RUNNING状态
      // 2.SHUTDOWN状态但是任务队列非空
      // 3.runState >= TIDYING 线程池已经停止了或在停止了
      if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && !workQueue.isEmpty()))
        return;

      // 只能是以下情形会继续下面的逻辑:结束线程池。
      // 1.SHUTDOWN状态,这时不再接受新任务而且任务队列也空了
      // 2.STOP状态,当调用了shutdownNow方法

      // workerCount不为0则还不能停止线程池,而且这时线程都处于空闲等待的状态
      // 需要中断让线程“醒”过来,醒过来的线程才能继续处理shutdown的信号。
      if (workerCountOf(c) != 0) { // Eligible to terminate
        // runWoker方法中w.unlock就是为了可以被中断,getTask方法也处理了中断。
        // ONLY_ONE:这里只需要中断1个线程去处理shutdown信号就可以了。
        interruptIdleWorkers(ONLY_ONE);
        return;
      }

      final ReentrantLock mainLock = this.mainLock;
      mainLock.lock();
      try {
        // 进入TIDYING状态
        if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
          try {
            // 子类重载:一些资源清理工作
            terminated();
          } finally {
            // TERMINATED状态
            ctl.set(ctlOf(TERMINATED, 0));
            // 继续awaitTermination
            termination.signalAll();
          }
          return;
        }
      } finally {
        mainLock.unlock();
      }
      // else retry on failed CAS
    }
  }

7. submit()

补充说明一点,submit()实际上也是通过调用execute()实现的,源码如下:

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

(三) 关闭“线程池”

shutdown这个方法会将runState置为 SHUTDOWN ,会终止所有空闲的线程。
shutdown()的源码如下:

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    // 获取锁
    mainLock.lock();
    try {
        // 检查终止线程池的“线程”是否有权限。
        checkShutdownAccess();主要区别在于shutdown调用的是 interruptIdleWorkers 这个方法,而shutdownNow实际调用的是Worker类的 interruptIfStarted 方法:
        // 设置线程池的状态为关闭状态。
        advanceRunState(SHUTDOWN);
        // 中断线程池中空闲的线程。
        interruptIdleWorkers();
        // 钩子函数,在ThreadPoolExecutor中没有任何动作。
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        // 释放锁
        mainLock.unlock();
    }
    // 尝试终止线程池
    tryTerminate();
}

说明:shutdown()的作用是关闭线程池。

shutdownNow方法将runState置为STOP。和shutdown方法的区别,这个方法会终止所有的线程。

public List<Runnable> shutdownNow() {
  List<Runnable> tasks;
  final ReentrantLock mainLock = this.mainLock;
  mainLock.lock();
  try {
    checkShutdownAccess();
    // STOP状态:不再接受新任务且不再执行队列中的任务。
    advanceRunState(STOP);
    // 中断所有线程
    interruptWorkers();
    // 返回队列中还没有被执行的任务。
    tasks = drainQueue();
  }
  finally {
    mainLock.unlock();
  }
  tryTerminate();
  return tasks;
}

主要区别在于shutdown调用的是 interruptIdleWorkers 这个方法,而shutdownNow实际调用的是Worker类的 interruptIfStarted 方法:

private void interruptIdleWorkers(boolean onlyOne) {
  final ReentrantLock mainLock = this.mainLock;
  mainLock.lock();
  try {
    for (Worker w : workers) {
      Thread t = w.thread;
      // w.tryLock能获取到锁,说明该线程没有在运行,因为runWorker中执行任务会先lock,
      // 因此保证了中断的肯定是空闲的线程。
      if (!t.isInterrupted() && w.tryLock()) {
        try {
          t.interrupt();
        } catch (SecurityException ignore) {
        } finally {
          w.unlock();
        }
      }
      if (onlyOne)
        break;
    }
  }
  finally {
    mainLock.unlock();
  }
}
void interruptIfStarted() {
  Thread t;
  // 初始化时state == -1
  if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
    try {
      t.interrupt();
    } catch (SecurityException ignore) {
    }
  }
}

这就是前面提到的Woker类实现AQS的主要作用。

注意:shutdown方法可能会在finalize被隐式的调用。

线程池状态

本章介绍线程池的生命周期。在”Java多线程系列–“基础篇”01之 基本概念”中,我们介绍过,线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态。线程池也有5种状态;然而,线程池不同于线程,线程池的5种状态是:Running, SHUTDOWN, STOP, TIDYING, TERMINATED。

线程池状态定义代码如下:

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;

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 ctlOf(int rs, int wc) { return rs | wc; }

说明
ctl是一个AtomicInteger类型的原子对象。ctl记录了”线程池中的任务数量”和”线程池状态”2个信息。
ctl共包括32位。其中,高3位表示”线程池状态”,低29位表示”线程池中的任务数量”。

RUNNING    -- 对应的高3位值是111。
SHUTDOWN   -- 对应的高3位值是000。
STOP       -- 对应的高3位值是001。
TIDYING    -- 对应的高3位值是010。
TERMINATED -- 对应的高3位值是011。

围绕ctl变量有一些操作,了解这些方法是看懂后面一些晦涩代码的基础:

/**
 * 这个方法用于取出runState的值 因为CAPACITY值为:00011111111111111111111111111111
 * ~为按位取反操作,则~CAPACITY值为:11100000000000000000000000000000
 * 再同参数做&操作,就将低29位置0了,而高3位还是保持原先的值,也就是runState的值
 * 
 * @param c
 *            该参数为存储runState和workerCount的int值
 * @return runState的值
 */
private static int runStateOf(int c) {
  return c & ~CAPACITY;
}


/**
 * 这个方法用于取出workerCount的值
 * 因为CAPACITY值为:00011111111111111111111111111111,所以&操作将参数的高3位置0了
 * 保留参数的低29位,也就是workerCount的值
 * 
 * @param c
 *            ctl, 存储runState和workerCount的int值
 * @return workerCount的值
 */
private static int workerCountOf(int c) {
  return c & CAPACITY;
}

/**
 * 将runState和workerCount存到同一个int中
 * “|”运算的意思是,假设rs的值是101000,wc的值是000111,则他们位或运算的值为101111
 * 
 * @param rs
 *            runState移位过后的值,负责填充返回值的高3位
 * @param wc
 *            workerCount移位过后的值,负责填充返回值的低29位
 * @return 两者或运算过后的值
 */
private static int ctlOf(int rs, int wc) {
  return rs | wc;
}

// 只有RUNNING状态会小于0
private static boolean isRunning(int c) {
  return c < SHUTDOWN;
}

线程池各个状态之间的切换如下图所示:

1. RUNNING

(01) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态!
道理很简单,在ctl的初始化代码中(如下),就将它初始化为RUNNING状态,并且”任务数量”初始化为0。

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

2. SHUTDOWN

(01) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(02) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

3. STOP

(01) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(02) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4. TIDYING

(01) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(02) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5. TERMINATED

(01) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(02) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

拒绝策略介绍

线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施。
当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭。第二,任务数量超过线程池的最大限制。

线程池共包括4种拒绝策略,它们分别是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy和DiscardPolicy。

AbortPolicy         -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
CallerRunsPolicy    -- 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy -- 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy       -- 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

线程池默认的处理策略是AbortPolicy!

拒绝策略对比和示例

下面通过示例,分别演示线程池的4种拒绝策略。

1. DiscardPolicy 示例

import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;

public class DiscardPolicyDemo {

    private static final int THREADS_SIZE = 1;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {

        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 设置线程池的拒绝策略为"丢弃"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

        // 新建10个任务,并将它们添加到线程池中。
        for (int i = 0; i < 10; i++) {
            Runnable myrun = new MyRunnable("task-"+i);
            pool.execute(myrun);
        }
        // 关闭线程池
        pool.shutdown();
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果

task-0 is running.
task-1 is running.

结果说明:线程池pool的”最大池大小”和”核心池大小”都为1(THREADS_SIZE),这意味着”线程池能同时运行的任务数量最大只能是1”。
线程池pool的阻塞队列是ArrayBlockingQueue,ArrayBlockingQueue是一个有界的阻塞队列,ArrayBlockingQueue的容量为1。这也意味着线程池的阻塞队列只能有一个线程池阻塞等待。
根据”“中分析的execute()代码可知:线程池中共运行了2个任务。第1个任务直接放到Worker中,通过线程去执行;第2个任务放到阻塞队列中等待。其他的任务都被丢弃了!

2. DiscardOldestPolicy 示例

import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;

public class DiscardOldestPolicyDemo {

    private static final int THREADS_SIZE = 1;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {

        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 设置线程池的拒绝策略为"DiscardOldestPolicy"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

        // 新建10个任务,并将它们添加到线程池中。
        for (int i = 0; i < 10; i++) {
            Runnable myrun = new MyRunnable("task-"+i);
            pool.execute(myrun);
        }
        // 关闭线程池
        pool.shutdown();
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(200);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果

task-0 is running.
task-9 is running.

结果说明:将”线程池的拒绝策略”由DiscardPolicy修改为DiscardOldestPolicy之后,当有任务添加到线程池被拒绝时,线程池会丢弃阻塞队列中末尾的任务,然后将被拒绝的任务添加到末尾。

3. AbortPolicy 示例

import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.RejectedExecutionException;

public class AbortPolicyDemo {

    private static final int THREADS_SIZE = 1;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {

        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 设置线程池的拒绝策略为"抛出异常"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());

        try {

            // 新建10个任务,并将它们添加到线程池中。
            for (int i = 0; i < 10; i++) {
                Runnable myrun = new MyRunnable("task-"+i);
                pool.execute(myrun);
            }
        } catch (RejectedExecutionException e) {
            e.printStackTrace();
            // 关闭线程池
            pool.shutdown();
        }
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(200);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(某一次)运行结果

java.util.concurrent.RejectedExecutionException
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1774)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:768)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:656)
    at AbortPolicyDemo.main(AbortPolicyDemo.java:27)
task-0 is running.
task-1 is running.

结果说明:将”线程池的拒绝策略”由DiscardPolicy修改为AbortPolicy之后,当有任务添加到线程池被拒绝时,会抛出RejectedExecutionException。

4. CallerRunsPolicy 示例

import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;

public class CallerRunsPolicyDemo {

    private static final int THREADS_SIZE = 1;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {

        // 创建线程池。线程池的"最大池大小"和"核心池大小"都为1(THREADS_SIZE),"线程池"的阻塞队列容量为1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 设置线程池的拒绝策略为"CallerRunsPolicy"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 新建10个任务,并将它们添加到线程池中。
        for (int i = 0; i < 10; i++) {
            Runnable myrun = new MyRunnable("task-"+i);
            pool.execute(myrun);
        }

        // 关闭线程池
        pool.shutdown();
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(某一次)运行结果

task-2 is running.
task-3 is running.
task-4 is running.
task-5 is running.
task-6 is running.
task-7 is running.
task-8 is running.
task-9 is running.
task-0 is running.
task-1 is running.

结果说明:将”线程池的拒绝策略”由DiscardPolicy修改为CallerRunsPolicy之后,当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到”线程池正在运行的线程”中取运行。

转载自:http://www.cnblogs.com/skywang12345/p/java_threads_category.html
参考:http://www.tuicool.com/articles/bYFzyeI
http://ifeve.com/java-threadpoolexecutor/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值