基本概念
《设计模式之禅》将对象池作为一种新的设计模式来看待,在重复生成对象的操作影响性能的时候适合将对象池化,这样看线程池算是对象池模式的一个典型应用了,其他应用还有DBCP、C3P0等数据库连接池,实际开发中如果使用对象池,可以用common-pool工具包来辅助实现,方便快捷。
使用线程池的好处:
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
工厂类Executors可以建立几种常用的线程池:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
另外还有workStealingPool和scheduledThreadPool,其实都是调用了ThreadPoolExecutor类来新建线程池,下面分析下这个类。
ThreadPoolExecutor参数:
先看看jdk源码中对参数的说明:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize—核心线程数
核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。
核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。 - maximumPoolSize—最大线程数
当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。 - workQueue—任务队列
保存等待执行的任务的阻塞队列,可以选择一下几种:
- ArrayBlockingQueue
基于数组的有界阻塞队列,FIFO。 - LinkedBlockingQueue
基于链表的阻塞队列,FIFO;静态工厂方法Executors.newFixedThreadPool()即使用该队列。 - SynchronousQueue
不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入会一直处于阻塞状态,吞吐量通常会高于LinkedBlockingQueue。静态工厂方法Executors.newCachedThreadPool()即使用该队列。 - PriorityBlockingQueue
- ArrayBlockingQueue
- keepAliveTime
当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。 - allowCoreThreadTimeout
是否允许核心线程空闲退出,默认值为false。 - queueCapacity
任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。
任务提交:
- submit()
需要返回信息,其内部还是调用的execute() - execute()
线程池按以下顺序执行任务:
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
- 若线程数小于最大线程数,创建线程
- 若线程数等于最大线程数,抛出异常,拒绝任务
参数设置
实际应用场景一般不会直接调用Executors直接这几种线程,而是根据响应需求和系统资源综合考虑,
IO密集型和CPU密集型
我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等。这种类型的任务越多,花在任务切换的时间就越多,导致CPU执行任务的时间减少。所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
IO密集型,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度),涉及到网络、磁盘IO的任务都是IO密集型任务。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
参考:
http://blog.youkuaiyun.com/miclung/article/details/7231553
http://www.cnblogs.com/jinzhiming/p/5120623.html
http://www.blogjava.net/bolo/archive/2015/01/20/422296.html