这一章开讲任务执行。绝大多数并发程序的工作都可以分解为抽象的、互不相关的工作单元,称之为任务(Task)。

使用java线程来执行任务

以web服务器的实现举例, 此时将用户的一次连接,当做一个独立的任务。

  1. 单线程顺序执行所有任务。
ServerSocket socket = new ServerSocket(80);
while(true) {
Socket connection = socket.accept();
handleRequest(connection);
}

这是最简单的方式,但效率也是最低的。通常在处理单用户的批量任务的时候,才适用。

  1. 为每一个任务启动一个线程来执行。
ServerSocket socket = new ServerSocket(80);
while(true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
}
new Thread(task).start();
}

这种方式的线程数没有限制,没有限制的线程数有如下几个缺点。a.每一个任务都要创建和销毁线程,带来额外开销;b.当线程数过多时,会占用过多的内存,会导致CPU大量的上下文切换;c.当线程数超过系统限制时,会导致系>统崩溃,通常的结果是OutOfMemoryError。

java的Executor Framework

Executor Framework提供了标准的方式,将任务的描述提交和任务的执行解耦。在同样的任务描述和提交的方式下,通过采用不同的executor实现类,可以实现不同的执行策略,单线程顺序执行、线程池执行等等。 Executor可以>方便用来实现生产者消费者模式,其中,提交任务的线程可以看做是生产者(生产需要被执行的任务),而执行任务的线程则可以看做是消费者。

  1. 使用Executor实现web服务器。

//不同的Executor,可以实现不同的执行策略,但任务提交方式是相同的。

Executor exec = ...;
ServerSocket socket = new ServerSocket(80);
while(true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
}
exec.execute(task);
}
  1. 什么是执行策略。

    • 任务会在哪个线程中被执行?
    • 任务会按照什么顺序被执行?FIFO,LIFO,按照优先级?
    • 多少个任务被同时执行?
    • 允许多少个任务缓存在队列中等待被执行?
    • 如果系统负载过重,哪些任务应该被放弃执行,又应该怎样通知应用程序?
    • 在执行任务之前和任务执行之后,应该做什么样的操作?
  2. 需要管理Executor的生命周期怎么办, 比如想要关闭Executor?答:使用ExecutorService。

  3. 执行调度任务,可以使用ScheduledThreadPoolExecutor。与Timer相比,它更优秀。比如a.ScheduledThreadPoolExecutor可以使用多个线程执行任务;b.可以更好地处理执行任务中遇到异常的情况。

  4. 需要任务的执行结果怎么办?使用Callable和Future。

  5. 提交了一系列的任务,希望在这些任务有执行结果的时候就立刻获取结果?用Future的get方法可以做到,但是使用ExecutorCompletionService可以做得更好, ExecutorCompletionService。ExecutorCompletionService维护一>个BlockingQueue用来保存已经执行完成的任务,并通过实现FutureTask的子类,在任务结束时将任务加入到该队列中。这样一来,又是一个生产者消费者模型,执行任务的ExecutorCompletionService可以看做是生产者,而处理已经完成任务的线程可以看做是消费者。

private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
  1. 需要批量提交任务,并获取这些任务的执行结果?使用ExecutorService接口的invokeAll方法, 但需要注意的是, 在AbstractExecutorService的实现中,这个方法忽略了任务执行过程中抛出的异常。
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
for (Future<T> f : futures) {
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done)
for (Future<T> f : futures)
f.cancel(true);
}
}

invokeAll方法还有一个指定超时时间的版本,其实现方式是通过在执行过程中不断减时间。

public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
if (tasks == null || unit == null)
throw new NullPointerException();
long nanos = unit.toNanos(timeout);
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks)
futures.add(newTaskFor(t)); long lastTime = System.nanoTime(); // Interleave time checks and calls to execute in case
// executor doesn't have any/much parallelism.
Iterator<Future<T>> it = futures.iterator();
while (it.hasNext()) {
execute((Runnable)(it.next()));
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
if (nanos <= 0)
return futures;
} for (Future<T> f : futures) {
if (!f.isDone()) {
if (nanos <= 0)
return futures;
try {
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
return futures;
}
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
}
}
done = true;
return futures;
} finally {
if (!done)
for (Future<T> f : futures)
f.cancel(true);
}
}
  1. 需要批量提交任务,并且只要这些任务中有一个执行完成就获取结果?使用ExecutorService接口的invokeAny方法, 事实上,在AbstractExecutorService的实现中,invokeAny就是使用ExecutorCompleteService实现的。
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null)
throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0)
throw new IllegalArgumentException();
List<Future<T>> futures= new ArrayList<Future<T>>(ntasks);
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this); // For efficiency, especially in executors with limited
// parallelism, check to see if previously submitted tasks are
// done before submitting more of them. This interleaving
// plus the exception mechanics account for messiness of main
// loop. try {
// Record exceptions so that if we fail to obtain any
// result, we can throw the last exception we got.
ExecutionException ee = null;
long lastTime = timed ? System.nanoTime() : 0;
Iterator<? extends Callable<T>> it = tasks.iterator(); // Start one task for sure; the rest incrementally
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1; for (;;) {
Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
long now = System.nanoTime();
nanos -= now - lastTime;
lastTime = now;
}
else
f = ecs.take();
}
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
} if (ee == null)
ee = new ExecutionException();
throw ee; } finally {
for (Future<T> f : futures)
f.cancel(true);
}
}

读Java Concurrency in Practice. 第六章.的更多相关文章

  1. Java Concurrency in Practice 读书笔记 第十章

    粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...

  2. Java Concurrency in Practice 读书笔记 第二章

    第二章的思维导图(代码迟点补上):

  3. 初读"Thinking in Java"读书笔记之第六章 --- 访问权限控制

    包:库单元 包内包含有一组类,他们在单一的名字空间下被组织在一起. 通过import ***.***.*可以将某个包下的所有类导入到当前文件中. 每个Java源文件最多只能有一个public类,且名称 ...

  4. Java Concurrency In Practice

    线程安全 定义 A class is thread-safe if it behaves correctly when accessed from multiple threads, regardle ...

  5. java并发编程实战(java concurrency in practice)

    第一章   线程共享进程范围内的资源,但每个线程都有各自的程序计数器.栈以及局部变量等. 多个线程可以同时调度到多个CPU上运行.   线程的优势? 在服务应用程序中,可以提升资源利用率以及系统吞吐率 ...

  6. Java Concurrency In Practice -Chapter 2 Thread Safety

    Writing thread-safe code is managing access to state and in particular to shared, mutable state. Obj ...

  7. Java Concurrency in Practice——读书笔记

    Thread Safety线程安全 线程安全编码的核心,就是管理对状态(state)的访问,尤其是对(共享shared.可变mutable)状态的访问. shared:指可以被多个线程访问的变量 mu ...

  8. “全栈2019”Java多线程第三十六章:如何设置线程的等待截止时间

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 下一章 "全栈2019"J ...

  9. Java Concurrency In Practice - Chapter 1 Introduction

    1.1. A (Very) Brief History of Concurrency motivating factors for multiple programs to execute simul ...

随机推荐

  1. C#中判断一个集合是另外一个集合的子集

    有这样的两个集合: string[] bigArr = new string[] { "a", "b", "c" };string[] sm ...

  2. AsyncTask实现多任务多线程断点续传下载

    这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下. 一.AsyncTask实现断点续传 二.AsyncT ...

  3. 【转】Android类动态加载技术

    http://www.blogjava.net/zh-weir/archive/2011/10/29/362294.html Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的 ...

  4. [C/C++] zltabout(带缩进的格式化输出)v1.0。能以相同的代码绑定到 C FILE 或 C++流

    作者:zyl910 一.缘由 在写一些生成文本的程序时,经常需要使用带缩进的格式化输出的功能.以前为此写过不少类似的函数,可惜它们的可重用性很差. 这是因为——1) C语言的FILE*不支持重定向到自 ...

  5. libevent

    libevent doc example #include <event2/event.h> void cb_func(evutil_socket_t fd, short what, vo ...

  6. Updating Protobuf and GRPC in Golang

    转自: http://russmatney.com/techsposure/update-protobuf-golang-grpc/ TL;DR: When protobuf updates, all ...

  7. selenium实战练习之:粉丝反馈表单的自动化脚本

    链接 粉丝反馈表 要求 服务质量5颗星 喜欢的内容选择各种公开课 对交流群的意见需要填写 留下自己正确的联系方式 点击提交 断言 不需要断言,能符合上面的要求成功提交就可以

  8. Nagios 安装配置

    ##Debian 6 安装server # apt-get install nagios* 一路设置好各种密码,成功后访问 http://ip/nagios3 既可,默认用户*nagiosadmin* ...

  9. Ehcache 使用

    自从Ehcache 到了1.2+的版本,就支持分布式缓存了 Spring + Hibernate的结构 ,ehcache的对这几个框架的支持较好,就采用这个缓存方案 下面是配置文件: <ehca ...

  10. C#集合 -- Equality和Order插件

    在前面的文章C#相等性比较和C#排序比较中,我已经叙述了类型相等,类型哈希,和类型比较的.NET标准协议.实现了这些协议的类型在一个字典或者列表中也可以正常工作.但是需要注意的是: 只有当类型的Equ ...