一、什么是线程池

为了避免系统频繁地创建和销毁线程,让创建出来的线程可以进行复用,这时就可以使用线程池,在线程池中,长期保持几个线程处于激活状态,当需要使用线程的时候,不在是直接创建线程,而是去线程池中拿取可用线程进行操作;反正,完成操作后,不需要去销毁线程,而是将线程放回线程池中。即总结来说:使用线程池后,创建线程变成从线程池中拿取线程,销毁线程变成了向线程池中归还线程。

二、java中线程池的使用

所有线程池,均在java.util.concurrent包中,其中重要的类和接口有:ThreadPoolExecutor表示一个线程池。Executors扮演线程池工厂的角色,通过Executors可以获得一个拥有特定功能的线程池。

其中创建线程池的方法如下:

public static ExecutorService newFixedThreadPool(int nThreads);

public statis ExecutorService newSingleThreadExecutor();

public static ExecutorService newCacheThreadPool();

public static ScheduledExecutorService newSingleThreadScheduledExecutor();

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);

三、各类线程池的比较

  1. newFixedThreadPool()是创建一个固定线程数量的线程池,当有新任务提交时,线程池中若有空闲线程,则立即执行,若无空闲线程,任务被暂存在一个任务队列,等待线程空闲时,便处理任务队列中的任务。
  2. newSingleThreadExecutor()是创建一个只有一个线程的线程池,当有新任务提交时,线程池中若有空闲线程,则立即执行,若无空闲线程,任务被暂存在一个任务队列,等待线程空闲时,按先入先出的顺序执行队列中的任务。
  3. newCacheThreadPool() 创建一个可根据实际情况调整线程数量的线程池,也就是说线程池中的线程数量是不固定的;若有空闲线程,优先复用空闲线程,如果当任务提交时,线程池中无空闲线程,则会创建新的线程来完成任务。所有线程在当前任务执行完成后,将返回线程池复用。
  4. newSingleThreadScheduledExecutor() 创建一个线程的线程池,此线程池扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性的执行某个任务
  5. newScheduledThreadPool() 创建一个可以指定线程数量的线程池,功能同上。

四、线程池的核心实现

查看源码可知,无论是newFixedThreadPool(),newSingleThreadExecutor(),还是newCacheThreadPool方法,其内部实现,都是使用了ThreadPoolExecutor ,其源码实现如下:

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



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


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

通过源码再来看看ThreadPoolExecutor的构造方法:

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

其中各参数含义如下:

  • corePoolSize:指定线程池中线程数量。
  • maximumPoolSize:指定了线程池中最大线程数量
  • keepAliveTime:当线程池线程数量超过corePoolSize时,多余的空闲线程的存活时间。即,超过corePoolSize的空闲线程,在多长时间内,会被销毁
  • unit:keepAliveTime的单位
  • workQueue:任务队列,被提交但尚未被执行的任务
  • threadFactory:线程工厂,用于创建线程,一般用默认的即可
  • handler:拒绝策略。当任务太多来不及处理,如何拒绝任务

其中参数workQueue指被提交但未执行的任务队列,他是一个BlockingQueue接口的对象,仅用来存放Runnable对象;只要分为一下几类:

  • 直接提交的队列:改功能由SynchronousQueue对象提供 。SynchronousQueue是一种特殊的BlockingQueue。SynchronousQueue没有容量,每一个插入操作都要等待一个相应的删除操作,反之亦然;SynchronousQueue不会被真实的保存,一旦有新任务,直接提交给线程池去执行,没有空闲线程就创建新的线程,当线程池中线程数达到最大值,则执行拒绝策略。
  • 有界的任务队列:使用ArrayBlockingQueue实现。当有新任务提交时,如果线程池中的实际线程数小于corePoolSize,则优先创建新的线程,若大于corePoolSize,则会加入到等待线程。若线程队列已满,无法加入,并且在总线程数不大于maximumPoolSize的前提下,创建新的线程执行任务,若线程数大于maximumPoolSize,则执行拒绝策略。
  • 无界的任务队列:由LinkedBlockingQueue类实现,与有界队列相比,除非系统资源耗尽,否则无界队列不存在任务入队失败的情况。当有新的任务时,系统的线程数小于corePoolSize时,线程池会生成新的线程执行任务,但当系统的线程数达到了corePoolSize后,不会继续增加,再有新任务时,任务直接进入队列等待。
  • 优先任务队列:优先任务队列是带有优先级的队列。通过PriorityBlockingQueue实现,可以控制任务执行先后顺序,是一种特殊的无界队列。

五、线程池中的拒绝策略

当任务量超过系统实际承载能力时,就使用拒绝策略;拒绝策略可以说是系统超负荷运行时的补救措施。

JDK内置拒绝策略如下:

  •  AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
  • CallerRunPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
  • DiscardOledestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
  • DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不给予任何处理。如果允许任务丢失,可使用此策略