第四章 现代并发(三)
一 控制执行
1任务建模
对任务建模的三种办法——Callable和Future接口以及FutureTask类
1)Callable接口
代表一段可以调用并返回结果的代码 典型用法是匿名实现类
是SAM(单一抽象方法)类型的示例——这是java7把函数作为一等类型作为值或一等类型的概念
2)Future接口
表示异步任务 是还没有完成的任务给出的未来结果
Future的主要方法
get()——用来获取结果。如果结果还没准备好 get会被阻塞直到它能取得结果
cancel()——在运算结束前取消
idDone()——调用者用它来判断运算是否结束
3)FutureTask类
是future接口的常用实现类 实现了Runnable接口 意味着FutureTask可以由执行者调度
提供两个很方便的构造器 一个以Callable为参数 另一个以Runnable为参数
2 ScheduledThreadpoolExecutor
是线程池类中的重中之重——功能多样 接受任务并把他们安排给线程池里的线程
线程池的大小可以预定义 也可自定义
所安排的任务可以定期执行 也可只运行义词
STPE扩展了ThreadpoolExecutor类(不具备定期调度能力)
二 分支/合并框架
分支/合并框架完全是为了实现线程池中任务的自动调度
关于分支/合并框架的重要事实和基本原理
引入一种新的执行者服务 成为ForkJoinpool
ForkJoinpool服务处理一种比线程“更小”的并发单元ForkJoinTask
ForkJoinTask是一种由ForkJoinpool以更轻量化的方式所调度的抽象
通常使用两种任务
“小型”任务是那种无需处理器耗费太多时间就可以直接执行的任务
”大型“任务是那种需要再直接执行前进行分解的任务
提供了支持大型任务分解的基本方法 还有自动调度和重新调度的能力
其关键特性之一就是这些轻量的任务都能够生成新的ForkJoinTask实例 这些实例将仍由执行它们父任务的线程池来安排调度 为分而治之
1ForkJoinTask与工作窃取
ForkJoinTask由ForkJoinpool调度安排,ForkJoinPool是专为这些轻量任务设计的新型执行者服务 该服务维护每个线程的任务列表 并当某个任务完成时 它能把挂在满负荷线程上的任务重新安排到空闲线程上去
采取这种”工作窃取“的算法可以解决大小不同的任务所导致的调度问题
2 并行问题
可以用分支/合并解决的问题
1)模拟大量简单对象的运动 比如例子效果
2)日志文件分析
3)从输入中计数的数据操作 比如mapreduce操作
以下列表检查问题及其子任务 可以去确定是否能用分支/合并来解决这个问题
1)问题的子任务是否无需与其它子任务有显式的协作或同步也可以工作
2)子任务是不是不会对数据进行修改 只是经过计算得出些结果
3)对于子任务来说 分而治之是不是很自然的事 子任务是不是会创建更多的子任务 而且它们要比派生出它们的任务粒数更细
三 java内存模型
两个基本概念列出最重要的规则
1)之前发生(Happens-Before)这种关系表明一段代码块在其他代码开始之前就已经全部完成了
2)同步约束(Synchronizes-With)这意味着动作继续执行之前必须把它的对象视图和主内存进行同步
JMM的主要规则如下
1)在监测对象上的解锁操作与后续的锁操作之间存在同步约束关系
2)对易失性(volatile)变量的写入与后续对该变量的读取之间存在同步约束关系
3)如果动作A受到动作B的同步约束 则A在B之前发生
4)如果在程序中的线程内A出现在B之前 则A在B之前发生
前两个规则时”先放后取“——一个线程在写入时持有的锁要在其他操作(包括读取)能够获取锁之前被释放掉
关于敏感行为的一些规则
1)构造方法要在那个对象的终结器开始运行之前完成(一个对象被终结之前必须已经构造完整)
2)开始一个线程的动作受到这个新线程的第一个动作的同步制约
3)Thread.join()受到被合并的线程的最后一个(和其它全部)动作的同步制约
4)如果X在Y之前发生 并且Y在Z之前发生(传递性)