java并发编程——线程池

256 阅读5分钟

为了避免重复的创建线程,线程池的出现可以让线程进行复用。通俗来讲,当有任务提交时,就会向线程池拿一个线程,当任务完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。


ThreadPoolExecutor:

ThreadPoolExecutor是线程池的实现类。

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

构造器参数解析:

corePoolSize:线程池的核心池大小,在创建线程池之后,线程池默认没有任何线程。除非调用了prestartAllCoreThreads()或prestartCoreThread()方法,这两个方法是预创建线程的方法,即在没有任务到来之前会创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务提交,就会创建一个线程去执行任务,当线程池的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列中。

maximumPoolSize:线程池的最大线程数。如果阻塞队列满了,并且已创建的线程数小于最大线程数,则线程池会在创建新的线程执行。

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,当线程池中的线程数大于corePoolSize时,如果一个线程空闲时间达到keepAliveTime,线程则会终止,直到线程池中的线程数不大于corePoolSize。如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。

unit:参数keepAliveTime的时间单位。

workQueue:阻塞队列,用来存储等待执行的任务。阻塞队列一般有以下几种:

ArrayBlockingQueue;//数组结构的有界阻塞队列,先进先出,创建时必须指定大小
LinkedBlockingQueue;//链表结构的无界阻塞队列,先进先出
SynchronousQueue;//不存储元素的阻塞队列,每次插入操作必须等到另一个线程调用移除操作

threadFactory:线程工厂,主要用来创建线程

handler:拒绝处理任务策略,有以下四种取值。

ThreadPoolExecutor.AbortFactory;//丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy;//丢弃任务,但不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy;//丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy;//由调用线程处理该任务

线程池的任务处理策略


首先取得当前线程池中的线程总数,与核心池大小比较,

  • 如果小于核心池大小,将通过addWorker()方法创建线程执行
  • 如果大于,则提交到阻塞队列
  • 如果阻塞队列已满,则把任务直接提交到线程池
  • 如果当前线程数达到最大线程数,则提交失败,执行拒绝策略,否则分配线程执行。

线程池的关闭

ThreadPoolExecutor提供了两个方法关闭线程池:

shutdown();//不会立即终止线程池,等所有的任务和阻塞队列的任务都执行完才终止,但不再接受新的任务。
shutdownNow();//立即终止线程池,并尝试打断正在执行的任务,并清空阻塞队列,返回尚未执行的任务

线程池的状态

volatile int runState;
static final int RUNNING = 0;//运行状态
static final int SHUTDOWN = 1;//关闭状态
static final int STOP = 2;//停止
static final int TERMINATED = 3;//终止

  • 创建线程池后,初始状态为RUNNING
  • 如果调用了shutdown(),则线程池处于SHUTDOWN状态,此时线程池不再接受新的任务,它会等所有任务执行完毕,最后终止。
  • 如果调用了shutdownNow(),线程池处于STOP状态,此时线程池不能接受新的任务,并且去尝试终止正在执行的任务,返回没有执行的任务列表。
  • 当线程池处于SHUTDOWN或STOP状态,并且所以工作线程已经销毁,阻塞队列已经清空或执行完之后,线程池被设置为TERMINATED状态

常见的四种线程池

FixedThreadPool:固定大小的线程池

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

固定大小的线程池,该线程池corePoolSize和maximumPoolSize相等,阻塞队列为LinkedBlockingQueue。固定大小的线程池,不存在线程数量的变化。

SingleThreadExecutor:单线程线程池

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

单线程线程池,只有一个线程的线程池,阻塞队列用LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,等空闲时再去执行,按照先进先出的顺序执行。该线程池可保证任务按照被加入顺序执行。

CachedThreadPool:缓存线程池

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

缓存线程池,缓存的线程默认存活60秒。线程池的corePoolSize大小为0,最大线程数为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue,是一个直接提交的阻塞队列,它总会迫使线程池增加新的线程去执行任务。当线程的空间时间超过60s,则工作线程将会终止被回收。当提交新任务时,如果没有空闲线程,则创建新线程执行任务。

ScheduledThreadPool:定时线程池

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

定时线程池,可用于周期性地执行任务,通常用于周期性的同步数据。

参考资料:浅入深理解Java线程池及线程池的如何使用