把程序抽象成多个任务。web
以独立的客户请求为边界。就是一个请求一个任务。编程
糟糕的响应性和吞吐量。安全
结论:bash
不足:服务器
Java提供了Executor框架来执行任务。基于生产者-消费者模式。提交任务就是操做至关于生产者,执行任务的线程至关于消费者。(解耦,削峰)并发
public interface Executor {
void execute(Runnable command);
}
复制代码
任务的提交代码散布在整个程序的业务代码中。
执行策略则统一交由框架处理。框架
执行策略中定义了任务执行的"What,Where,When,How"等方面,包括:分布式
经过将任务提交与任务的执行策略分离,有助于在部署阶段选择与可用硬件资源最匹配的执行策略。ide
Executor任务执行框架将"为每个任务分配一个线程"策略编程基于线程池的策略。
类库提供了一个灵活的线程池及一些有用的默认配置。如newFixedThreadpool。性能
Executor扩展了ExecutorService接口,添加了一些用于生命周期管理的方法。
public interface ExecutorService extends Executor {
/**
* 平缓的关闭过程:再也不接受新任务,等待已经提交的任务执行完成。
*/
void shutdown();
/**
* 粗暴的关闭过程:它将尝试取消全部运行中的任务,不在启动队列中还没有开始执行的任务。
*/
list<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit);
}
复制代码
JAVA中提供Timer来管理定时任务。
ScheduledThreadPoolExecutor更优质的管理定时任务。
不适用于分布式环境。
本章提供一些示例来发掘在一个请求中的并行性。
假设页面 = 文本标签 + 图片
以下代码串行的执行渲染。
public class SingleThreadRenderer {
void renderPage(CharSequence source) {
renderText(source);
List<ImageData> imageData = new ArrayList<ImageData>();
for (ImageInfo imageInfo : scanForImageInfo(source))
imageData.add(imageInfo.downloadImage());
for (ImageData data : imageData)
renderImage(data);
}
}
复制代码
Runnable做为基本的任务表现形式。缺陷:1.无返回值。2.不能抛出一个受检查异常。
它是任务更好的抽象,描述了一个任务的返回值和异常。
public interface Callable<V> {
V call() throws Exception;
}
复制代码
它表示一个任务的生命周期,并提供了相应的方法来判断任务是否已经完成或取消。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws Exception;
V get(long timeout, TimeUnit unit);
}
复制代码
将渲染过程分解成两个任务,一个是渲染全部的文本,一个是下载全部图像。
代码略。
复制代码
渲染文本和渲染图片并发执行。
上例中通常渲染文本的速度远远高于渲染图片的速度,程序最终和串行执行效率差异不大,代码确变得更复杂了。
只有大量相互独立且同构的任务能够并发进行处理时,才能体现出性能的提高。
提交一组任务,简单的写法。
@Test
public void test() throws Exception{
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList();
for (int i=0; i<5; i++){
final int param = i;
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(param * 1000);
return "result" + param;
}
});
futures.add(future);
}
for (int i=4; i>0; i--) {
System.out.println(futures.get(i).get());
}
}
复制代码
CompletionService将Executor和BlockingQueue的功能融合。你能够将Callable任务提交给它执行,而后使用相似队列操做的take和poll方法来得到已完成的结果。
书上的示例:略。
@Test
public void test() throws Exception{
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<String> completionService = new ExecutorCompletionService<>(executor);
for (int i=4; i>0; i--){
final int param = i;
completionService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(param * 1000);
return "result" + param;
}
});
}
for (int i=0; i<4; i++) {
System.out.println(completionService.take().get());
}
}
输出:
result1
result2
result3
result4
复制代码
为单个任务设置时间。
@Test
public void singleTaskTest(){
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() {
try {
Thread.sleep(2000L);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("任务执行完毕...");
return "singleTask.";
}
});
try {
System.out.println(future.get(1, TimeUnit.SECONDS));
}catch (TimeoutException e){
System.out.println("任务超时...");
future.cancel(true); // 这句话的是否注销影响运行状况,原理未知?
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
}
复制代码
未多个任务设置超时时间。
本章主要是介绍了Java的Executor框架的优势和一些常见需求。 还有对任务的划分粒度,要根据业务场景分析任务边界。