线程池与Executor框架
线程池的作用
线程池提供了一种限制和管理资源(包括执行一个任务)。每个线程池还维护一些基本统计信息,例如完成任务的数量。总体上讲,他有以下好处:
-
降低资源消耗:通过反复利用已经创建的线程降低线程创建和销毁造成的额消耗.
-
提高响应速度:当任务到达时,任五可以不需要的等到线程创建就能立即执行。
-
提高线程的可管理性:线程时稀缺资源,如果无限的创建,会造成计算机资源的浪费,还会降低系统的稳定性,使用线程池可以实现统一的管理分配。
Executor框架
Executor框架是Java5引入的,使用Executor可以更好的使用线程,使用效率更高,除此之外还有助于避免this逃逸(指构造函数在返回该对象引用之前就被其他的线程持有)
Executor的组成部分
任务
执行的任务需要实现Runnble接口或者Callable接口(Runnable接口不会返回结果但是Callable接口可以返回结果)。Runnable接口或者Callable接口实现类可以被ThreadPoolExector或者ScheduledThreadPoolExecutor自行
任务执行
通过上面的图可以知道任务的执行的核心是Executor和继承Executor接口的ExecutorService接口,ScheduledThreadPoolExecutor和ThreadPoolExecutor这两个关键类实现了ExecutorService接口.
注意:通过查看ScheduledThreadPoolExecutor源码可以发现ScheduledThreadPoolExecutor实际上是继承了ThreadPoolExeecutor并实现了ScheduledExecutorService,而ScheduledExecutorService又实现了ExecutorService。
结果返回
Future接口以及Future接口的实现类FutureTask类。当我们把Runnable接口或者Callable接口的实现类提交(调用usubmit方法)给ThreadPoolExector时,会返回一个FutureTask对象.
public <T> Future<T> submit(Runnable task, T result) {
return schedule(Executors.callable(task, result), 0, NANOSECONDS);
}
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay,
TimeUnit unit) {
if (callable == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
Executor框架使用示意图
1.主线程首先需要创建实现Runnable或者Callable接口的任务对象.备注:工具类Executors可以实现Runnable对象和Callable对象之间的相互转换。(Executors.callable(Runnable task)或者Executor.callable(Runnable task,Object result))
2.将创建的任务Runnable或者Callable对象直接交给ExecutorService执行 (ExecutorService.execute(Runnable command));湖泊这也可以把Runnable对象或者Callable对象提交给E小ecotrService执行(ExecutorService.submit(Callable task)).
3.执行ExecutorService.submit(Callable task),将返回一个实现Future接口的对象。
4.主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel(boolean mayInterruptIfRunning)来取消任务的执行。
ThreadPoolExecutor源码解读
根据源码可以知道,ThreadPoolExecutor有四个构造方法,但是其他几个都时建立在下面解析的这个构造方法上。
/**
*用给定的初始值创建一个新的[@code](https://my.oschina.net/codeo) threadpoolExecutor
*参数。
*
*[@param](https://my.oschina.net/u/2303379) corepoolsize保留在池中的线程数,偶数
*如果它们是空闲的,除非设置了[@code](https://my.oschina.net/codeo) allowcorethreadTimeout
*[@param](https://my.oschina.net/u/2303379) maximumpoolsize允许的最大线程数
*池
*[@param](https://my.oschina.net/u/2303379) keepalivetime当线程数大于
*核心,这是多余空闲线程的最长时间
*将在终止前等待新任务。
*@param unit@code keepalivetime参数的时间单位
*@param workqueue用于在任务之前保存任务的队列
*执行。此队列将只包含@代码可运行
*由@code execute方法提交的任务。
*@param threadfactory执行器时要使用的工厂
*创建新线程
*@param handler执行被阻止时要使用的处理程序
*因为达到了线程边界和队列容量
*@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
*或@code threadFactory或@code handler为空
*/
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.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;
}
通过上面的图片和代码片段的解析可以知道该方法的参数比较多,而且都比较麻烦,所以不建议使用构造函数去创建ThreadPoolExecutor对象,但是可以使用下面的几种方式创建ThreadPoolExecutor对象.
FixedThreadPool源码解读
/**
* 创建重用固定数量线程的线程池
* 使用提供的在需要时创建新线程。在任何时候,
*最多N个线程将处于活动处理状态
*任务。如果在所有线程
*活动,它们将在队列中等待,直到线程
*可用。如果任何线程在
*在关闭前执行,如果
*需要执行后续任务。池中的线程将
*存在,直到它显式执行服务关闭
* @param nThreads 线程池的线程数
* @param threadFactory 创建线程工厂类
* @return 返回新创建的线程池
* @throws NullPointerException if threadFactory is null
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
或者
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool的执行的方式大致如《并发编程艺术的》的示意图所示:
说明:
-
如果当前的运行的线程小于corePoolSize,则创建新的线程来执行任务
-
当前运行的线程等于corePoolSize后,将任务加入LinkedBlockingQueue<Runnable>
-
线程执行完成步骤1的任务之后,会在循环中反复的从LinkedBlockingQueue<Runnable>中获取任务来执行。
FixedThreadPool使用的LinkedBlockingQueue(队列容量为Integr.MAX_VALUE,如果构造函数没有设置大小的时候),有些文章说FixedThreadPool是无界队列是不正确的。除此之外还会有如下的影响:
1.当线程中的线程达到了CorePoolSize后,新任务将在无界对垒中等待,因此线程不会超过corePoolSize;
2.由于1,使用的没设置大小的LinkedBlockingQueue,将使得maxinumPoolSize将是一个无效的参数。
3.由于1,2使用不设置大小的LinkedBlockingQueue,将使得keepAliveTime将是无效的参数
4.运行中FixedThreadPool(未执行shudown()或者shudownNow())就不会拒绝任务.
SigleThreadExecutor
1.当前的运行的线程数少云corePoolSize,则创建一个新的线程执行任务。
2.当前线程池中有一个运行的线程后,将任务加入LinkedBlockQueue
3.线程执行完1中的任务后,会在循环中反复的从LinkedBlockingQueue中获取任务来执行。
CacheThreadPool
CacheThreadPool是一个会根据需要创建新线程的线程池。
/**
* 创建一个线程池,根据需要创建新线程,但会在先前构建的线程可用时重用它,
*并在需要时使用提供的ThreadFactory创建新线程。
* @param threadFactory 创建新线程使用的factory
* @return 新创建的线程池
* @throws NullPointerException 如果threadFactory为空
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
//CachedThreadPool的corePoolSize被设置为空(0),maximumPoolSize被设置为Integer.MAX.VALUE,即它是无界的,这也就意味着如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新的线程。极端情况下,这样会导致耗尽cpu和内存资源。
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ScheduledThreadPoolExecutor
scheduledThreadPoolExecutor主要用來在給定的延遲運行任務,或者定期執行任務。ScheduledThreadPoolExecutor使用任務隊列DelayQueue封裝了一個PriorityQueue,PrioprityQueue會對隊列中的任務進行排序,執行所需時間短的放到最前面執行,如果執行時間一樣則使先提交的先執行。
ScheduledThreadPoolExecutor和Time的比較
-
Time對系統時鐘的變化比較敏感,ScheduledThreadPoolExecutor則相反。
-
Timer只有一個執行執行現場,因此長時間運行任務可以延遲其他任務。ScheduledThreadPoolExecutor可以配置任意現場,此外,如果你想(通過ThreadFactory),你可以完全控制創建的綫程。
-
在TimerTask中抛出的运行异常会杀死一个线程,从而导致司机:(计划任务将不再运行,ScheduledThreadExecutor不仅捕获运行异常,还允许您再需要时处理他们(通过重写afterExecute方法)。抛出的异常将被取消,其他的任务可以继续运行。
ScheduledThreadPoolExecutor运行机制
ScheduledThreadPoolExecutor的执行主要包括两部分:
1.当调用ScheduledThreadPoolExecutor的scheduleAtFixRate()或者scheduleWirhFixedDelay()时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask.
2.线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务.
ScheduledThreadPoolExecutor未了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改:
- 使用DelayQueue作为任务队列
- 获取任务的方法不同
- 执行周期任务后,增加了额外的处理.
各种线程的使用场景
-
FixedThreadPool: 适用于为了满足资源管理需求,限制当前线程数量的应用场景。它适用于负载比较重的服务器;
-
SingleThreadExecutor: 适用于保证顺序地执行各个任务并且在任意时间点,不会有多个线程是活动的应用场景。
-
CachedThreadPool: 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器;
-
ScheduledThreadPoolExecutor: 适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景,
-
SingleThreadScheduledExecutor: 适用于需要单个后台线程执行周期任务,同时保证顺序地执行各个任务的应用场景。