第6章 任务执行

如果可运行的线程数量多于可用处理器的数量,那么有引动线程将闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量线程在竞争CPU资源时还将产生其他的性能开销。

任务是一组逻辑工作单元,而线程则是使任务异步执行的机制。我们已经分析了两种通过线程来执行任务的策略,即把所有任务放在单个线程中串行执行,以及将每个任务放在各自的线程中执行。这两种方式都存在一些严格的限制:串行执行的问题在于其糟糕的响应性和吞吐量,而“为每个任务分配一个线程”的问题在于资源管理的复杂性。

在执行策略中定义了任务执行的“What、Where、When、How”等方面,包括

  • 在什么(What)线程中执行任务?
  • 任务按照什么(What)顺序执行(FIFO、LIFO、优先级)?
  • 有多少个(How Many)任务能并发执行?
  • 在队列中有多少个(How Many) 任务在等待执行?
  • 如果系统由于过载而需要拒绝一个任务,那么应该选择哪一个(Which)任务?另外,如何(How)通知应用程序有任务被拒绝?
  • 在执行一个任务之前或之后,应该进行哪些(What)动作?

通过重用现有的线程而不是创建新线程,可以在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销。另一个额外的好处是,当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行,从而提高了响应性。

ExecutorService的生命周期有3种状态:运行、关闭和已终止。

shutdown方法将执行平缓的关闭过程:不再接受新的任务,同时等待已经提交的任务执行完成——包括那些还未开始执行的任务。shutdownNow方法将执行粗暴的关闭过程:它将尝试取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。

可以通过两种方法来关闭Web服务器:在程序中调用stop,或者以客户端请求形式向Web服务器发送一个特定格式的HTTP请求。

Timer存在一些缺陷,因此应该考虑使用ScheduledThreadPoolExecutor来代替它。

Timer在执行所有定时任务时只会创建一个线程。如果某个任务的执行时间垞,那么将破坏其他TimerTask的定时精确性。例如某个周期TimerTask需要每10ms执行一次,而另一个TimerTask需要执行40ms,那么这个周期任务或者在40ms任务执行完成后快速连续地调用4次,或者彻底“丢失”4次调用(取决于它是基于固定速率来调度还是基于固定延时来调度)。线程池能弥补这个缺陷,它可以提供多个线程来执行延时任务和周期任务。

Timer的另一个问题是,如果TimerTask抛出了一个未检查的异常,那么Timer将表现出糟糕的行为。Timer线程并不捕获异常,因此当TimerTask抛出未检查的异常时将终止定时线程。这种情况下,Timer也不会恢复线程的执行,而是会错误地认为整个Timer都被取消了。

Timer支持基于绝对时间而不是相对时间的调度机制,因此任务的执行对系统时钟变化很敏感,而ScheduledThreadPoolExecutor只支持基于相对时间的调度。

Executor执行的任务有4个生命周期阶段:创建、提交、开始和完成。由于有些任务可能要执行很长的时间,因此通常希望能够取消这些任务。在Executor框架中,已提交但尚未开始的任务可以取消,但对于那些已经开始执行的任务,只有当它们能响应中断时,才能取消。取消一个已经完成的任务不会有任何影响。

Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。在Future规范中包含的隐含意义是,任务的生命周期只能前进,不能后退。

在将Runnable或Callable提交到Executor的过程中,包含了一个安全发布过程,即将Runnable或Callable从提交线程发布到最终执行任务的线程。类似地,在设置Future结果的过程中也包含了一个安全发布,即将这个结果从计算它的线程发布到任务通过get获得它的线程。

当在多个工人之间分配异构的任务时,还有一个问题就是各个任务的大小可能完全不同,如果将两个任务A和B分配给两个工人,但A的执行时间是B的10倍,那么整个过程也只能加速9%。最后,当在多个工人之间分解任务时,还需要一定的任务协调开销:为了使任务分解能提高性能,这种开销不能高于并行性实现的提升。

FutureRenderer使用了两个任务,其中一个负责渲染文本,另一个负责下载图像。如果渲染文本的速度远高于下载图像的速度(可能性很大),那么程序的最终性能与串行执行时的性能差别不大,而代码却变得更复杂了。当使用两个线程时,至多能将速度提高一倍。因此虽然做了许多工作来并发执行异构任务以提高并发度,但从中获得的并发性却是十分有限的。

只有当大量相互独立且同构的任务可以并发进行处理时,才能体现出将程序的工作负载分配到多个任务中带来的真正性能提升。

时间: 2024-12-23 11:30:19

第6章 任务执行的相关文章

数据库系统实现 第六章 查询执行

第六章 查询执行 查询执行也就是操作数据库的算法 一次查询的过程: 查询-->查询编译(第七章)-->查询执行(第六章)-->数据 查询编译预览 查询编译可以分为三个步骤: a)分析:构造分析树,用来表达查询和它的结构 b)查询重写,分析树被转化为初始查询计划,通常是代数表达式,之后初始的查询计划会被优化为一个时间更小的计划 c)物理计划生成,将查询计划转化成物理的计划, 为了选择更好的查询计划,需要判断 1)查询哪一个代数的等价形式是最有效的 2)对选中形式的每一个操作,所使用的算法选

Thinking In Java笔记(第四章 控制执行流程)

第四章 控制执行流程 Java中使用了C的所有流程控制语句.在Java中涉及的关键字包括if-else,while,do-while,for,return,break,continue以及选择语句switch.然而Java不支持goto语句(该语句引起了许多的反对意见),但是Java仍然可以类似goto那样跳转. 4.1 True和False 所有的条件语句都利用条件表达式的真假来决定执行的路径.Java中不允许我们将一个数字作为boolean值使用,虽然C和C++中是允许的("非零"

第六章——根据执行计划优化性能(1)——理解哈希、合并、嵌套循环连接策略

原文:第六章--根据执行计划优化性能(1)--理解哈希.合并.嵌套循环连接策略 前言: 本系列文章包括: 1. 理解Hash.Merge.Nested Loop关联策略. 2.在执行计划中发现并解决表/索引扫描. 3. 介绍并在执行计划中发现键查找并解决它们. 对于性能优化,需要集中处理以下的问题: 1. 为你的环境创建性能基线. 2. 监控现在的性能并发现瓶颈. 3. 解决瓶颈以便得到更好的性能. 一个预估执行计划是描述查询将会如何执行的一个蓝图,而一个实际执行计划就是一个查询执行时实际发生的

第六章——根据执行计划优化性能(3)——键值查找

原文:第六章--根据执行计划优化性能(3)--键值查找 前言: 本文为本系列最后一篇,介绍键值查找的相关知识. 键值查找是具有聚集索引的表上的一个书签查找,键值查找用于SQLServer查询一些非键值列的数据.使用非聚集索引的查询不会有键值查找,但是所有键值查找会伴随非聚集索引出现.这里特别提醒的是键值查找总是伴有嵌套循环关联. 准备工作: 下面将创建一个表,通过执行计划看看键值查找的不同效果.为了产生键值查找,需要两件事情: 1.  聚集索引 2.  非聚集索引 当你在非聚集索引键值上有谓词时

第六章——根据执行计划优化性能(2)——查找表/索引扫描

原文:第六章--根据执行计划优化性能(2)--查找表/索引扫描 前言: 在绝大部分情况下,特别是从一个大表中返回少量数据时,表扫描或者索引扫描并不是一种高效的方式.这些必须找出来并解决它们从而提高性能,因为扫描将遍历每一行,查找符合条件的数据,然后返回结果.这种处理是相当耗时耗资源的.在性能优化过程中,一般集中于: 1.  CPU 2.  Network 3.  磁盘IO 而扫描操作会增加这三种资源的开销. 准备工作: 下面将创建两个表来查看不同的物理关联操作的不同影响.创建脚本已经在本系列的第

Java编程思想---第四章 控制执行流程

第四章  控制执行流程 就像有知觉的生物一样,城西必须在执行过程中控制它的世界并作出选择,在Java中,你要使用执行控制语句来作出选择. 4.1 true和false 所有的条件语句都利用条件表达式的真假来决定执行路径.如a==b,它用操作符==来判断a的值是否等于b的值,返回一个true或false. 4.2 if-else if-else语句是控制程序流程的最基本形式,其中else是可选的,所以可以按下面的两种形式来使用: if(Boolean-expression) statement 或

[Thinking in Java]第4章-控制执行流程

4.1 if-else 4.2 迭代 4.3 Foreach语法 4.4 return 4.5 break和continue 4.6 switch 目录 4.1 if-else if-else中的else是可选的,有两种形式使用if: if (Boolean-expression) statement 或 if (Boolean-expression) statement1 else statement2 需要注意的是,Boolean-expression不能是数字,其实后面的while,for

《JAVA编程思想》学习笔记——第四章 控制执行流程

true和false 所有条件语句都利用条件表达式的真或假来决定执行路径. if-else if (Boolean-expression) statement 或 if (Boolean-expression) statement else statement 迭代 while, do-while和for来控制循环,有时将他们划分为迭代语句(iteration statement).语句会重复执行,直到起控制作用的布尔表达式(Booleanexpression)得到"假"的结果为止.

第十三章 自动执行作业

一.控制前后台作业 jobs:列出当前环境的后台或暂停的作业 fg.bg.kill:控制指定的作业,作业用"%+作业号"来指定 nohup:运行一个即便账号logout都不会被kill,而是被init进程接管的作业 二.at命令--预指定单独一次的作业执行 #at [ -c | -k | -s -q Quene] [ -m ] [ -f File ] { -t Date | Time [ Day ] [ increment ] } 1)三种运行at命令的格式 a)#at Time co