JDK 可以创建使用Executors 创建5种类型的线程池
Executors.newCachedThreadPool();
该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先 使用可复用的线程。 若所有线程均工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前安任务执行完毕后,将返回线程池进行复用。
Executors.newFixedThreadPool(nThreads)
该方法创建一个固定数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行,若没有,则新的任务会被 暂存在一个任务队列中,待有空闲线程时,便处理在任务队列中的任务。
Executors.newSingleThreadExecutor()
该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存哎ige任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
Executors.newSingleThreadScheduledExecutor()
该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExectorService接口在ExecutorService接口之上扩展了在给定时间执行某任务的功能, 如在固定的延时之后执行或者周期性执行某个任务。
Executors.newScheduledThreadPool( corePoolSize)
该方法可以返回固定数量的线程池。
具体代码实现如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
threadFactory);
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
我们看下这个构造函数的源码及解释:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize 指定了线程的数量。
maximumPoolSize指定了线程池的最大数量
keepAliveTime 线程池中线程的数量超过了corePoolSize 的空闲线程存活的时间。
unit keepAliveTime的单位
workQueue 任务队列
threadFactory 线程工厂
handler 拒绝策略,来不及处理任务时,采用怎样的方式拒绝任务。
下面介绍下 workQueue 和handler
参数workQueue 指被提交但未执行的任务队列,它是一个BlockingQueue接口的对象,仅用于存放Runnable对象。 根据队列功能分类,在ThreadPoolExecutor的构造函数中可使用一下几种BlockingQueue。
直接提交的队列: 该功能由 SynchronousQueue对象提供。SynchronousQueue 是一个特殊的BlocingQueue。 它没有容量,每一个插入操作都要等待一个相应的删除操作,反之,每一个删除操作都要等待对应的插入操作。如皋市使用SynchronousQueue,提交的任务不会被真实的保存,而总是将新任务提交给线程执行,如果没有空闲的进程,则尝试创建新的进程,如果进程的数量已达到最大值,则执行拒绝策略。
有界的任务队列
有界的任务队列可以使用ArrayBlockingQueue实现。当使用有界队列时,若有新的任务需要执行,如果线程池的实际线程数小于corePoolSize,则会优先创建新的线程,若大于corePoolSize,则会将新任务假如等待队列。若等待队列已满,无法加入,在总线程数,不大于maximumPoolSize的前提下,创建新的进程执行任务。若大于maximumPoolSize,则执行拒绝策略。
无界的任务队列
无界的任务队列可以通过LinkedBlockingQueue类实现。与有界队列相反,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务到来,系统的线程数小于corePoolSize时,线程池会产生新的线程执行任务,但当系统的线程数达到corePoolSize后,就会继续增加。若后续仍有新的任务假如,而又没有空闲的线程资源,则任务直接进入对列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,知道耗尽系统内存。
任务优先队列
优先任务队列是带有执行优先级的队列,它通过PriorityBlockingQueue实现,可以控制任务的只想你个先后顺序。它是一个特殊的无界队列。
ThreadPoolExecutor 核心执行代码如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. 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.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
这个方法会有三步:
1 如果当前线程的数量小于corePoolSize,则执行addWorker 分配线程执行任务。
2 如果当前的线程数量大于corePoolSize,则将线程放进workQueue队列中,等待执行。
如果放入失败,则将任务直接提交,分配线程进行执行。
3 如果当前线程的数量已经达到了maximumPoolSize 则执行拒绝策略。
拒绝策略(下篇继续)