ThreadPoolExecutor是ExecutorService的一种实现,可以用若干已经池化的线程执行被提交的任务。使用线程池可以帮助我们限定和整合程序资源,尽可能避免创建新的线程来执行任务从而降低任务调用的开销,在执行大量异步任务的时候反而能获得更好的性能。此外,ThreadPoolExecutor还会维护一些统计信息,比如已完成的任务数量。
juc包的作者Doug Lea推荐程序员尽量使用更为便利的Executors类的工厂方法来配置线程池:
Executors.newCachedThreadPool():创建一个线程池,可以根据线程池的需要来创建任务。这个线程池比较适用执行周期较短且量大的异步任务,在调用execute(...)方法时如果线程池中存在闲置的线程,将复用闲置线程,否则创建一个新线程执行任务。如果线程的闲置时间超过60s,线程将被终止并从线程池内移除。因此,该线程池即便空闲时间再长,也不会有资源的消耗。
Executors.newFixedThreadPool(int nThreads):创建一个线程池,nThreads为池内线程的数量,池内最多同时有nThreads个线程并行处理任务,如果有新的任务提交到线程池,会先暂存在线程池中的无边界任务队列进行等待,直到有线程可用。如果有线程在执行期间因为错误提前终止,线程池将启动一个新的线程代替原先的线程继续处理任务队列中处于等待的任务。除非显式调用shutdown(),否则线程池中的线程将一直存在。
Executors.newSingleThreadExecutor():创建一个Executor,该Executor使用一个工作线程处理任务。如果线程在执行期间因为错误而终止,将启动一个新的线程代替原先的线程继续处理无边界任务队列中处于等待的任务。队列中的任务是按顺序执行,任何时刻都不会有多个任务处于活跃状态。与newFixedThreadPool(1)不同,newFixedThreadPool(int nThreads)生成的线程池,可以强转为ThreadPoolExecutor类型,再调用setCorePoolSize(int corePoolSize)方法设置核心线程数,而newSingleThreadExecutor()的实现类为FinalizableDelegatedExecutorService,无法直接设置核心线程数。
上面三种是较为常见的配置线程池的工厂方法,如果有需要根据业务场景特殊配置线程池的,请看下面的参数:
核心线程数和最大线程数ThreadPoolExecutor将根据corePoolSize(核心线程数)和maximumPoolSize(最大线程数)设置的边界自动调整线程池内工作线程的数量(通过getPoolSize()),corePoolSize可以通过getCorePoolSize()、setCorePoolSize(int corePoolSize)获取和设置核心线程数,maximumPoolSize可以通过getMaximumPoolSize()、setMaximumPoolSize(int maximumPoolSize)获取和设置最大线程数。当有新的任务提交时,如果工作线程少于核心线程数,将会创建一个新线程来执行该任务,即便其他工作线程处于闲置状态。如果工作线程多于corePoolSize但少于maximumPoolSize,则当任务队列满的时候才会创建新线程。如果corePoolSize和maximumPoolSize数值一样,则创建一个固定大小的线程池;如果将maximumPoolSize设置为Integer.MAX_VALUE,则线程池可以容纳任意数量的并发任务。
按需构造核心线程只有当有新任务到达时才会创建,但我们可以重写prestartCoreThread() 或者prestartAllCoreThreads()来预先启动核心线程。如果在构造一个线程池时,传入的任务队列已经存在任务,则需要线程池初始化完毕后,预先启动线程。
创建新线程使用ThreadFactory(线程工厂)创建新线程。如果没有特别指定,则使用Executors.defaultThreadFactory()作为默认的线程工厂,该线程工厂所创建的线程都位于相同的线程组(ThreadGroup)中,线程的优先级都是NORM_PRIORITY,线程守护状态都为false。通过提供不同线程工厂的实现,你可以修改线程名、线程组、线程优先级和守护状态等等。
活跃时间如果线程池中的线程数超过核心线程数,多出的线程如果空闲时间超出keepAliveTime(活跃时间)将会终止,回收不再活跃的线程。当有需要时,新的线程会重新创建。可以通过setKeepAliveTime(long time, TimeUnit unit)动态设置活跃时间。如果time设置为Long.MAX_VALUE,unit设置为TimeUnit.NANOSECONDS,那么多余的空闲线程将不会在关闭线程池之前回收。如果调用allowCoreThreadTimeOut(boolean value)传入的value为true,那么keepAliveTime将适用于核心线程,如果allowCoreThreadTimeOut为true且keepAliveTime不为0,核心线程的空闲时间超出活跃时间,核心线程也会被回收。
队列阻塞队列(BlockingQueue)允许在获取元素时陷入等待,直到有元素加入到队列中。调用阻塞队列方法时,有些方法不一定马上返回,可能会在未来某个时刻达成某些条件时返回。阻塞队列的方法伴随四种形式:
抛出异常。
返回特殊值,null或者false,具体视操作而定。
调用线程无限期陷入阻塞直到某些条件达成。