在线程中执行任务
-
大多数服务器应用都提供一种自然的任务边界选择方式:以独立的客户请求为边界
-
当为每个请求创建一个线程时,任务处理代码必须是线程安全的,因为有多个任务时会并发调用这段代码
-
无限制创建线程的缺陷:
- 线程生命周期开销非常高
- 资源消耗:活跃的线程会消耗内存,大量线程在竞争CPU时也会产生其他的性能开销
- 稳定性:可以存在线程的数量存在限制,超出限制会抛出OutOfMemory
Executor框架
-
Java类库中,任务执行的主要抽象不是Thread,而是Executor,基于生产者-消费者模式,提交任务的操作相当于生产者,执行任务的线程相当于消费者
-
线程池:管理一组同构工作线程的资源池,可以通过Executors以下静态工厂方法之一创建一个线程池
-
newFixedThreadPool:创建一个固定长度的线程池,每提交一个任务创建一个线程,直到达到线程池的最大数量,之后规模不再变化(如果某个线程发生了未预期的Exception而结束,线程池会补充一个新的线程)
-
newCachedThreadPool:创建一个可缓存的线程池,如果当前规模超过了处理需求,会回收空闲线程,规模不存在任何限制,使用SynchronousQueue
-
newSingleThreadExecutor:只有单个工作者线程,保证依照任务在队列中的顺序串行执行
-
newScheduledThreadPool:创建固定长度的线程池,以延时或定时方式执行任务,类似Timer
-
-
Executor的生命周期:
-
ExecutorService继承自Executor,添加了一些生命周期管理的方法(shutdown平缓关闭/shutdownNow粗暴关闭/awaitTermination等待到达终止状态…)
-
ExecutorService生命周期有3种状态:运行、关闭和已终止,所有任务完成后,ExecutorService到达终止状态,所以一般awaitTermination后立即调用shutdown,产生同步关闭ExecutorService的效果
-
Timer类:执行所有定时任务只会创建一个线程,TimerTask的执行时长太长会破坏其它TimerTask的定时准确性;抛出一个未检查的异常就终止定时线程了,整个Timer被取消
-
构建自己的调度服务:使用DelayQueue实现了BlockingQueue,管理一组Delayed对象,每个对象有一个延迟时间,为ScheduledThreadPoolExecutor提供调度功能
-
找出可利用的并行性
-
Callable:携带结果,比Runnable更好的抽象,认为主入口(call)将返回一个值并可能抛出一个异常
-
Executor执行的任务有4个生命周期阶段:创建、提交、开始、完成
-
Future代表一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等
-
ExecutorService的所有submit方法都将返回一个Future,从而将一个Runnable/Callable提交给Executor,并得到一个Future获取任务执行结果或取消任务;还可显式为某个Runnable/Callable实例化一个FutureTask
-
只有当大量相互独立且同构的任务可以并发进行处理时,才能体现出将程序的工作负载分配到多个任务中带来的真正性能提升
-
CompletionService:将Executor和BlockingQueue的功能融合在一起,已完成任务的结果会放到队列中通过take获取:
- ExecutorCompletionService实现了CompletionService,并将计算部分委托给一个Exector;提交任务时,将任务包装为一个QueueingFuture(FutureTask子类),再改写QueueingFuture的done方法,将结果放入QueueingFuture中的BlockingQueue中
-
为任务设置时限:
-
限时的future.get
-
ExecutorService限时的invokeAll:提交多个任务,获得一组按任务迭代器顺序排序的Future,从而将Future和其表示的Callable关联起来
-