ThreadPoolExecutor线程池创建方法有两种:
1、Executors提供了常用线程池的工厂方法;
2、通过ThreadPoolExecutor构造方法创建
一般使用Executors提供的默认线程池,当默认的执行策略不满足需求,那么可以通过ThreadPoolExecutor的构造函数来实例化一个对象,并根据自己的需求来定制,下面是ThreadPoolExecutor通用的构造函数:
1 public ThreadPoolExecutor(int corePoolSize,2 int maximumPoolSize,3 long keepAliveTime,4 TimeUnit unit,5 BlockingQueueworkQueue6 ThreadFactory threadFactory,7 RejectedExecutionHandler handler) {8 ...9 }
1、corePoolSize
线程池的基本大小,也就是线程池的目标大小,即在没有任务执行时线程池的大小;在工作队列满的情况下会创建超出这个数量的线程;
2、maximumPoolSize
可同时活动的线程数量的上限,在工作队列满时创建超出corePoolSize的线程数量;
3、keepAliveTime
存活时间,如果某个线程的空闲时间超过存活时间,那么将被标记为可回收的,并在当前线程池的线程超过了corePoolSize时,这个线程将被终止;
4、workQueue
工作队列,分为无界队列、有界队列和同步移交,用户存放待执行的任务;
只有当任务是相互独立时,为线程池或工作队列设置界限才是合理的。如果任务之间存在依赖,那么有界线程池或队列就可能导致线程“饥饿”死锁的问题。此时应该使用无界线程池。
5、RejectedExecutionHandler:饱和策略
JDK文档清楚的说明了什么情况下回执行饱和策略
If we cannot queue task, then we try to add a new thread. If it fails, we know we are shut down or saturated and so reject the task.
当工作队列已满,就尝试增加一个线程,如果失败就表示线程池已关闭或已饱和,那么将拒绝该任务
6、ThreadFactory:线程工厂
每当线程池需要创建一个新的线程时,都是通过线程的工厂方法(如下)去创建。
1 public interface ThreadFactory {2 Thread newThread(Runnable r);3 }
默认的线程工厂方法将创建一个新的、非守护的线程,并且不包含特殊的配置信息,如下:
1 static class DefaultThreadFactory implements ThreadFactory { 2 private static final AtomicInteger poolNumber = new AtomicInteger(1); 3 private final ThreadGroup group; 4 private final AtomicInteger threadNumber = new AtomicInteger(1); 5 private final String namePrefix; 6 7 DefaultThreadFactory() { 8 SecurityManager s = System.getSecurityManager(); 9 group = (s != null) ? s.getThreadGroup() :10 Thread.currentThread().getThreadGroup();11 namePrefix = "pool-" +12 poolNumber.getAndIncrement() +13 "-thread-";14 }15 16 public Thread newThread(Runnable r) {17 Thread t = new Thread(group, r,18 namePrefix + threadNumber.getAndIncrement(),19 0);20 if (t.isDaemon())21 t.setDaemon(false);22 if (t.getPriority() != Thread.NORM_PRIORITY)23 t.setPriority(Thread.NORM_PRIORITY);24 return t;25 }26 }
一般我们需要一个特定的线程池的名字,从而可以在错误日志信息中区分来自不同线程池的线程,就需要自己实现ThreadFactory接口,定制newThread方法。
7、定制ThreadPoolExecutor
通过ThreadPoolExecutor构造函数创建线程池后,仍然可以通过各种setter方法来修改大多数构造参数;如果线程池是通过Executors中的某个工厂方法提供的,那么可以通过强制类型转换为ThreadPoolExecutor以访问设置器。为避免给这种情况的发生,Executors提供了unconfigurableExecutorService方法。该方法对一个现有的ExecutorService方法进行包装,使其只暴露出ExecutorService方法,因此不能进行各种参数的配置,防止不信任代码对线程池进行修改。
8、扩展ThreadPoolExecutor
ThreadPoolExecutor是可以扩展的,子类可以重写beforeExecute、afterExecute和teminated方法。这些方法可以日志、计时、监视或统计信息收集的功能。任务无论是从run中正常返回还是抛出一个异常而返回,都会调用afterExecute方法。如果beforeExecute方法抛出一个RuntimeExeception,那么任务将不会执行,并且afterExecute方法也得不到调用。在线程池完成关闭操作时将调用terminate方法,也就是所有任务都已经完成并且所有工作者也已经关闭后。