在线程中执行任务
以任务执行为结构的应用程序设计的关键是找出清晰的任务边界,大多数服务器应用程序都是以独立的客户请求为界。
串行的执行任务
一种串行的web服务器,接受请求和提供服务都在同一线程中,这种串行的方式无法提供高吞吐率或快速响应。
显示地为任务创建线程
为在主循环中为每个请求的任务创建一个线程,将任务处理放在子线程进行处理。任务处理的代码必须是线程安全的,因为其有多个任务时会并发的调用。
无限制创建线程的不足
当请求量大时,使用new Thread会创建大量的线程,1线程的生命周期开销非常的高、2活跃的线程消耗内存资源,当可运行的线程数量大于cpu核心数时,大量闲置线程,占用内存、3系统对可创建的线程数有限制。
Executor框架
线程池接口,直接提交任务,框架安排调度线程执行。
基于Executor的web服务器
不再是每个处理任务生成一个线程,而是生成一个可执行的任务Runnable,将任务通过executor进行执行。
在调用线程中以同步方式执行所有任务的Executor,WithinThreaadExecutor,这个问题不是很明白。
执行策略
使用Executor代理Thread可以更灵活的制定任务执行的策略。
线程池
newFixedThreadPool 固定线程数量的线程池
newCachedThreadPool 缓存线程池
newSingleThreadExecutor 一个单线程,但是可以保证线程的存在性,如果异常结束后会再创建一个线程
newScheduledThreadPool 用来代替Timer
Executor的生命周期
使用shutdown或shutdownNow来结束Executor,awaitTermination判断线程池是否停止(shutdown方法被调用之后,或者参数中定义的timeout时间到达或者当前线程被打断)
延迟任务与周期任务
使用DelayQueue构建调度任务
找出可利用的并行性
在任务边界不是十分明显的时候,或者在一般的单线程程序中,我们仍然可以通过划分任务边界挖掘程序的并发性。
串行的页面渲染器
在同一线程中,先进行下载,再进行渲染。
携带任务的Callable与Future
Runnable是任务的基本形式,但是它不能返回一个值或者抛出一个异常,Future是任务返回的一个对象,可以通过这个对象阻塞的获取值。
使用Future实现页面渲染器
使用一个task来下载所有图像,task中迭代下载所有图像,然后通过get阻塞的获取下载的图像后,在主线程中进行迭代渲染。
在异构任务并行化中存在局限
两个异构的任务需要处理的进度不一样时,就会导致任务的执行不一致,不能完全充分的利用cpu性能,代码却变得更复杂了,所以最后将任务拆分为相互独立且同构的任务,确保这些任务适合并发处理。
CompletionService
使用Future的get方法获取计算结果,需要不停的轮询,方然虽然好,但是繁琐,于是有了CompletionService,将Executor与BlockingQueue结合,将执行完成后的结果add进BlockingQueue,然后直接获取。
使用CompletionService实现页面的渲染器
将每一幅图片作为一个任务,通过CompletionService执行,执行后先通过completionService的take获取Future,此时的Future是计算完成已经有结果的,在通过future的get方法获取最终结果,详细可见P107。
为任务设置时限
通过future的get方法设置时限,超过时限会抛出TimeoutException,捕获后需要取消任务,避免继续计算浪费算力。
旅行预订门户网站
通过invokeAll方法提交一组任务,然后返回一组future