结构化并发应用程序
1.1 串行执行任务
1.2 显示为任务创建线程
1.3 无限制创建线程的不足
- 线程生命周期的开销非常高。
- 资源消耗活跃的线程会消耗系统资源,尤其是内存。
- 稳定性
2.Executor框架
任务是一组逻辑工作单元,而线程则是使任务异步执行的机制。
串行执行线程的缺点:串行执行的问题在于其糟糕的响应性和吞吐量。
Executor是一个简单的接口,它支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程与执行过程解耦开来,并用Runnable来表示任务。
Executor基于生产者-消费者模式
.
2.1 给予EX的web服务器
2.2 执行策略
当希望用一种更灵活的执行策略时,我们应该使用Executor
2.3 线程池
线程池的优点:
- 通过重用现有的线程,而不是创建新的线程,可以在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销。
- 当请求到达时,工作线程通常已经存在,因此不会由于创建线程而延迟任务的执行,从而提高了响应速度。
- 通过适当调整线程池的大小,可以创建足够多的线程以便使处理器保持忙碌状态,同时还可以防止过多的线程相互竞争资源而使应用程序耗尽内存资源。
线程池的优点:
对应用程序的稳定性产生重大影响:web服务器不会在高负载情况下失败。由于服务器不会创建数千个线程老争夺有限的CPU和内存资源,因此服务器的性能将会平缓的降低。通过使用Executor,可以实现各种调优、管理、监视、记录日志、错误报告和其他功能,如果不使用任务执行框架,那么增加这些功能是非常困难的。
2.3.1 newFixedThreadPool
2.3.2 newCachedThreadPool
2.3.3 newSignleThreadPool
2.4 EX的生命周期
由于executor是异步来执行任务的,因此在任何时刻,之前提交任务的状态不是立即可见的。为了解决执行服务的生命周期问题,executor拓展了ExecutorService接口,添加了一些用于生命周期管理的方法。
ExecutorService的状态:运行、关闭和终止。
ExecutorService关闭后提交的任务将由“拒绝执行处理器(Rejected Execution Handler)”来处理,它会抛弃任务,或使得execute方法抛出一个未检查的拒绝执行异常。
2.5 延迟任务与周期任务
Timer类负责管理延迟任务以及周期任务。Timer类设计存在一些问题,可以使用ScheduledThreadPoolExecutor来代替。
Timer在执行所有的任务时只会创建一个线程,如果某个任务的执行时间很长,那么将破坏其他TimerTask的定时精确性。线程池能弥补这个缺点,它可以提供多个线程来执行延时任务和周期任务。
如果要构建自己的调度服务,那么可以使用DelayQueue,它实现了BlockingQueue,并为ScheduledThreadPoolExecutor提供调度服务。DelayQueue管理着一组delayed对象,每个对象都有一个相应的延迟时间:在DelayQueue中,只有某个元素逾期后,才能从DelayQueue中执行task操作。
3 找出可利用的并行性
3.1 串行的页面渲染器
在网页应用中,先为应用加载文本元素,再给用户下载图片等大文件。
3.2 携带任务结果的FT
Executor框架能使用Runnable作为基本的任务表示形式。Runnable是一种有很大局限的抽象,虽然run能写入到日志文件或者将结果放入某个共享的数据结构,但它
3.3 使用FT实现页面渲染
FutureTask的使用
NewRealData 中实现了Callable接口,并重写了call()方法。
package com.neil.future;
import java.util.concurrent.Callable;
/**
* Created by Administrator on 2017/2/21.
*/
public class NewRealData implements Callable<String>{
private String data;
public NewRealData(String data){
this.data = data;
}
@Override
public String call() throws Exception {
Thread.sleep(1000);
return data;
}
}
FutureTask有一个参数是Callable的构造函数,将 FutureTask的对象传递给ExecutorService创建的线程池,并在线程池中提交任务,调用FutureTask的get方法获取计算结果。
package com.neil.future;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
* Created by Administrator on 2017/2/21.
*/
public class NewApplication {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<String>(new NewRealData("name"));
ExecutorService executor = Executors.newFixedThreadPool(1);//使用线程池
//执行FutureTask. 相当于client.reques(name)
executor.submit(futureTask);
//等待处理
Thread.sleep(2000);
//如果call没有执行完成,这里依然会等待,大概经过3秒就可以获取到结果
System.out.println("数据" + futureTask.get()); //获取真实的数据
}
}
3.4 异构任务
3.5 EX和BQ
如果向EX提交了一组计算任务,如果想在计算完成后获取结果,那么可以选择保留与每个任务关联的Future,然后反复使用get方法,同时将参数timeout制定为0,从而通过轮询来判断任务是否完成。但是这种方式比较繁琐。、
CompletionService将Executor和BlockingQueue的功能融合在一起。可以使用Callable任务提交给它来执行,然后使用类似于队列操作的take和poll等方法来获得已完成的结果,从而这些结果会在完成时被封装为Future。ExecutorCompletionService实现了CompletionService,并将计算结果部分委托给一个Excutor。
多个ExecutorCompletionService可以个共享一个Executor可以创建一个针对特定计算私有,又能共享一个公共Executor的ExecutorCompletionService。因此,CompletionService的作用就像对于一组