一、在线程中执行任务
1、串行地执行任务
当服务器正在处理请求时,新的连接必须等待直到请求处理完毕。
如果请求阻塞时间过长,用户将认为服务器不可用。
2、显式地为任务创建线程
通过每个请求创建一个新的线程来提供服务,从而实现高响应性。
需要创建大量线程时:
- 线程生命周期开销非常高,线程创建、销毁需要代价
- 资源消耗,活跃的线程消耗系统资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将闲置。
大量闲置线程占用内存,给垃圾回收带来压力,并在竞争cpu资源时产生性能开销。 - 稳定性。可创建线程数据存在一个限制。受JVM启动参数、Thread构造函数中请求栈大小、底层操作系统对线程的限制等。
在一定范围内,增加线程可以提高系统吞吐率,超过这个范围,在创建更多的线程只会降低程序的执行速度,甚至系统崩溃。
二、Executor框架
Executor提供了一种标准的方法将任务的提交过程与执行过程解耦,并用Runnable来表示任务。
public interface Executor {
void execute (Runnbale command);
}
提供了对生命周期的支持,以及统计信息收集、应用程序管理机制和性能监视等机制。
Executor基于生产者-消费者模式,提交任务相当于生产者,执行任务的线程相当于消费者。
1、线程池
管理一组同构工作线程的资源池。比“为每个任务分配一个线程”优势更:
- 通过重用现有的线程而不是创建新线程,
- 可以在处理多个请求时分摊创建线程和销毁的开销。不会等待线程创建而延迟任务执行。
- 创建足够的线程使处理器保持忙绿,防止过多线程相互竞争资源而使应用程序耗尽内存或失败
-
newSingleThreadPool:单线程的Executor,创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。
可以确保任务在队列中串行执行。
- newFixedThreadPool:创建一个固定大小的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量。
- newCachedThreadPool:创建一个可缓存的线程池,如果线程池当前规模超过了处理需求时,将回收空闲线程;当需求增加时,课添加新的线程,线程池规模无限制
-
newScheduledThreadPool:创建一个固定大小的线程池,而且以延迟或定时的方式来执行任务,类似Timer
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
2、Executor的生命周期
ExecutorService接口添加了生命周期管理方法。
ExecutorService的生命周期有三种状态:运行、关闭、已终止。
在初始化创建时处于运行状态。
shutdown方法将执行平缓关闭过程:不再接受新任务,同时等待已久提交的任务执行完成,包括还未开始执行的任务。
shutdownNow方法执行粗暴关闭过程:长手取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。
3、延迟任务与周期任务
三、找出可以利用的并行性
如果要使用Executor,必须将任务表述为一个Runnable。
1、携带结果的任务Callbale与Future
Executor框架使用Runnable作为其基本任务的表示形式。
Runnable存在问题:虽然run能写入到日志文件或者将结果放入某个共享的数据结构,但不能返回一个值或抛出一个受检查的异常。
许多任务实际上都是存在延迟计算--执行数据库查询、从网络上获取资源或计算某个复杂功能。
对于这种任务,Callable是一种更好的抽象:它认为主入口点将返回一个值,并可能抛出一个异常。
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Executor执行任务有4个生命周期阶段:创建、提交、开始、完成。
在Executor框架中,已提交尚未开始的任务可以取消,对于已经开始执行的任务,只有当它们能响应中断时,才能取消。
Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。