这一章开讲任务执行。绝大多数并发程序的工作都可以分解为抽象的、互不相关的工作单元,称之为任务(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. 關於imagick不得不說的一些事

    PHP建圖通常都用GD庫,因為是內置的不需要在服務器上額外安裝插件,所以用起來比較省心,但是如果你的程序主要的功能就是處理圖像,那麼就不建議用GD了,因為GD不但低效能而且能力也比較弱,佔用的系統資源 ...

  2. NEWS - InstallShield 2015 正式发布

    如果您需要为Windows®应用程序创建安装,InstallShield®便是您的最佳解决方案.在为桌面.服务器.云.Web和虚拟环境构建可靠的Windows Installer (MSI)和Inst ...

  3. C# 动态修改dll的签名 以及修改引用该dll文件的签名

    在读取RedisSessionStateProvider配置 提到用mono ceil 来修改程序集以及它的签名,里面GetPublicKey 和GetPubliKeyToken 方法里面那个字符串的 ...

  4. OpenCV相机标定和姿态更新

    原帖地址: http://blog.csdn.net/aptx704610875/article/details/48914043 http://blog.csdn.net/aptx704610875 ...

  5. 通过apt-get安装nvidia驱动

    标签:NVIDIA Driver apt 早前安装的NVIDIA显卡驱动在启动X Server的时候提示版本太新了,要求必须使用340.96的,而新的驱动都到了367 https://wiki.deb ...

  6. 10条现代EQ技术基础贴士(转)

    前言: 无论是追求复古的模拟音色还是高精度的透明音质,现代电脑音乐制作中层出不断的新EQ插件以其超强的频率塑形和个性化功能为音色的润色和重塑提供了无限可能. 虽然EQ并不是音频工程工具中最复杂的,但是 ...

  7. vim 多行注释消除注释,多行删除

    进入可视化模式: Ctrl+v 继续进入编辑模式: shift+i 注释: shift+# 注释生效: ESC 取消注释 d 删除 选中全部字符块区域,使用方向键上下右: 然后,按一下d

  8. EntityFramework 5.0 CodeFirst 教程04-查询,插入,更新,和删除数据

    ---------------------目录-------------------------- EntityFramework 5.0 CodeFirst 教程04-查询,插入,更新,和删除数据  ...

  9. Service Station - An Introduction To RESTful Services With WCF

    Learning about REST An Abstract Example Why Should You Care about REST? WCF and REST WebGetAttribute ...

  10. WPF 模板

    一.DataTemplate(数据模板)1.引用命名空间xmlns:别名="clr-namespace:命名空间" 2.调用命名空间下的类别和属性<Window.Resour ...