阿里代码规范中规定,线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程。这样做的好处是减少在创建和销毁所花的时间和系统开销。不使用线程池可能造成创建大量同类线程而导致消耗内存或则“过度切换”的问题。并且规定线程池不允许使用Executors创建。那么创建线程的方式基本就依赖于ThreadPoolExecutor此类了。
先了解下ThreadPoolExecutor的构造函数:
//构造函数使用默认的 DefaultThreadFactory 以及默认的AbortPolicy
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}
//使用默认的拒绝策略
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue, ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
}
//以上两个两个构造函数调用此函数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue, RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);
}
//以上三个函数调用此函数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue 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;
}
以上Overloading最终调用最后一个函数,集中解析下最后一个构造函数的参数就可以了。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
corepoolsize:核心线程数 线程池创建后激活的线程数量
maxinumpoolSize:最大线程数 线程池提交的线程数,如果队列已经满了,而且超出最大线程数,会使用拒绝策略
keepAliveTime和timeUnit配置使用表示超过核心线程数的线程最大的空闲存货时间和单位
blockingQueue:保存等待执行的任务的阻塞队列 参数可以如下
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,按FIFO原则进行排序
LinkedBlockingQueue:一个基于链表结构的阻塞队列,吞吐量高于ArrayBlockingQueue。
SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量高于LinkedBlockingQueue。
PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
threadFactory:通过线程工厂为每个创建出来的线程设置更有意义的名字
RejectedExecutionHandler:当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略还处理新提交的任务。它可以有如下四个选项:
AbortPolicy:直接抛出异常,默认情况下采用这种策略
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
DiscardPolicy:不处理,丢弃掉
有了以上定义好的数据,下面来看看内部是如何实现的 。整个思路总结起来就是 5 句话:
我们看下常用Executors的实现:
//固定大小的线程池,多余的线程放入linkedBlockingQueue中,然后从对列中取线程
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
//synchronousQueue不存储元素,有元素立即分配线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
//其实就是单位为1的fixedThreaPool
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
看了这么多实例我们可知,以上线程池如果线程过多可能会造成大量堆积待处理线程或则创建大量线程的问题。灵活使用ThreadPoolExecutor可以更加明确线程运行规则,避免资源耗尽的风险。
欢迎留言交流,共同讨论