第1章 介绍 1
1.1 并发的(非常)简短历史 1
1.2 线程的优点 3
1.3 线程的风险 5
1.4 线程无处不在 9
第1部分 基础 13
第2章 线程安全 15
2.1 什么是线程安全性 17
2.2 原子性 19
2.3 锁 23
2.4 用锁来保护状态 27
2.5 活跃度与性能 29
第3章 共享对象 33
3.1 可见性 33
3.2 发布和逸出 39
3.3 线程封闭 42
3.4 不可变性 46
3.5 安全发布 49
第4章 组合对象 55
4.1 设计线程安全的类 55
4.2 实例限制 58
4.3 委托线程安全 62
4.4 向已有的线程安全类添加功能 71
4.5 同步策略的文档化 74
第5章 构建块 79
5.1 同步容器 79
5.2 并发容器 84
5.3 阻塞队列和生产者-消费者模式 87
5.4 阻塞和可中断的方法 92
5.5 Synchronizer 94
5.6 为计算结果建立高效、可伸缩的高速缓存 101
第2部分 构建并发应用程序 111
第6章 任务执行 113
6.1 在线程中执行任务 113
6.2 Executor框架 117
6.3 寻找可强化的并行性 123
第7章 取消和关闭 135
7.1 任务取消 135
7.2 停止基于线程的服务 150
7.3 处理反常的线程终止 161
7.4 JVM关闭 164
第8章 应用线程池 167
8.1 任务与执行策略间的隐性耦合 167
8.2 定制线程池的大小 170
8.3 配置ThreadPoolExecutor 171
8.4 扩展ThreadPoolExecutor 179
8.5 并行递归算法 181
第9章 GUI应用程序 189
9.1 为什么GUI是单线程化的 189
9.2 短期的GUI任务 192
9.3 耗时GUI任务 195
9.4 共享数据模型 198
9.5 其他形式的单线程子系统 202
第3部分 活跃度,性能和测试 203
第10章 避免活跃度危险 205
10.1 死锁 205
10.2 避免和诊断死锁 215
10.3 其他的活跃度危险 218
第11章 性能和可伸缩性 221
11.1 性能的思考 221
11.2 Amdahl定律 225
11.3 线程引入的开销 229
11.4 减少锁的竞争 232
11.5 示例:比较Map的性能 242
11.6 减少上下文切换的开销 243
第12章 测试并发程序 247
12.1 测试正确性 248
12.2 测试性能 260
12.3 避免性能测试的陷阱 266
12.4 测试方法补遗 270
第4部分 高级主题 275
第13章 显式锁 277
13.1 Lock和ReentrantLock 277
13.2 对性能的考量 282
13.3 公平性 283
13.4 在synchronized和ReentrantLock之间进行选择 285
13.5 读-写锁 286
第14章 构建自定义的同步工具 291
14.1 管理状态依赖性 291
14.2 使用条件队列 298
14.3 显式的Condition对象 306
14.4 剖析Synchronizer 308
14.5 AbstractQueuedSynchronizer 311
14.6 java.util.concurrent的Synchronizer类中的AQS 314
第15章 原子变量与非阻塞同步机制 319
15.1 锁的劣势 319
15.2 硬件对并发的支持 321
15.3 原子变量类 324
15.4 非阻塞算法 329
第16章 Java存储模型 337
16.1 什么是存储模型,要它何用 337
16.2 发布 344
16.3 初始化安全性 349
附录A 同步Annotation 353
A.1 类Annotation 353
A.2 域Annotation和方法Annotation 353
参考文献 355
索引 359
代码清单 6
清单1.1 非线程安全的序列生成器 6
清单1.2 线程安全的序列生成器 7
清单2.1 一个无状态的Servlet 18
清单2.2 Servlet计算请求数量而没有必要的同步(不要这样做) 19
清单2.3 惰性初始化中存在竞争条件(不要这样做) 21
清单2.4 Servlet使用AtomicLong统计请求数 23
清单2.5 没有正确原子化的Servlet试图缓存它的最新结果(不要这样做) 24
清单2.6 缓存了最新结果的servlet,但响应性令人无法接受(不要这样做) 26
清单2.7 如果内部锁不是可重入的,代码将死锁 27
清单2.8 缓存最新请求和结果的servlet 31
清单3.1 在没有同步的情况下共享变量(不要这样做) 34
清单3.2 非线程安全的可变整数访问器 36
清单3.3 线程安全的可变整数访问器 36
清单3.4 数绵羊 39
清单3.5 发布对象 40
清单3.6 允许内部可变的数据逸出(不要这样做) 40
清单3.7 隐式地允许this引用逸出(不要这样做) 41
清单3.8 使用工厂方法防止this引用在构造期间逸出 42
清单3.9 本地的基本类型和引用类型的变量的线程限制 44
清单3.10 使用Thread Local确保线程封闭性 45
清单3.11 构造于底层可变对象之上的不可变类 47
清单3.12 在不可变的容器中缓存数字和它的因数 49
清单3.13 使用到不可变容器对象的volatile类型引用,缓存最新的结果 50
清单3.14 在没有适当的同步的情况下就发布对象(不要这样做) 50
清单3.15 如果Holder没有被正确发布,它将处于失败的风险中 51
清单4.1 使用Java监视器模式的简单线程安全计数器 56
清单4.2 使用限制确保线程安全 59
清单4.3 私有锁保护状态 61
清单4.4 基于监视器的机动车追踪器实现 63
清单4.5 类似于java.awt.Point的可变Point 64
清单4.6 DelegatingVehicleTracker使用不可变的Point类 64
清单4.7 将线程安全委托到ConcurrentHashMap 65
清单4.8 返回location集的静态拷贝,而非“现场(live)”的 66
清单4.9 委托线程安全到多个底层的状态变量 66
清单4.10 NumberRange类没有完整地保护它的不变约束(不要这样做) 67
清单4.11 可变的线程安全Point类 69
清单4.12 安全发布底层状态的机动车追踪器 70
清单4.13 扩展的Vector包含一个“缺少即加入”方法 72
清单4.14 非线程安全的“缺少即加入”实现(不要这样做) 72
清单4.15 使用客户端加锁实现的“缺少即加入” 73
清单4.16 使用组合(composition)实现“缺少即加入” 74
清单5.1 操作Vector的复合操作可能导致混乱的结果 80
清单5.2 使用客户端加锁,对Vector进行复合操作 81
清单5.3 迭代中可能抛出的ArrayIndexOutOfBoundsException 81
清单5.4 使用客户端加锁进行迭代 82
清单5.5 用Iterator对List进行迭代 82
清单5.6 迭代隐藏在字符串的拼接中(不要这样做) 84
清单5.7 ConcurrentMap接口 87
清单5.8 桌面搜索应用程序中的生产者和消费者 91
清单5.9 开始桌面搜索 92
清单5.10 恢复中断状态,避免掩盖中断 94
清单5.11 在时序测试中,使用CountDownLatch来启动和停止线程 96
清单5.12 使用FutureTask预载稍后需要的数据 97
清单5.13 Throwable强制转换为RuntimeException 98
清单5.14 使用信号量来约束容器 100
清单5.15 在一个细胞的自动系统中用CyclicBarrier协调计算 102
清单5.16 尝试使用HashMap和同步来初始化缓存 103
清单5.17 用ConcurrentHashMap替换HashMap 105
清单5.18 用FutureTask记录包装器 106
清单5.19 Memoizer最终实现 108
清单5.20 使用Memoizer为因式分解的servlet缓存结果 109
清单6.1 顺序化的Web Server 114
清单6.2 Web Server为每个请求启动一个新的线程 115
清单6.3 Executor接口 117
清单6.4 使用线程池的Web server 118
清单6.5 为每个任务启动一个新线程的Executor 118
清单6.6 Executor在调用线程中同步地执行所有任务 119
清单6.7 ExecutorService中的生命周期方法 121
清单6.8 支持关闭操作的Web Server 122
清单6.9 Timer的混乱行为 124
清单6.10 顺序地渲染页面元素 125
清单6.11 Callable interface和Future interface 126
清单6.12 ThreadPoolExecutor中newTaskFor的默认实现 126
清单6.13 使用Future等待图像下载 128
清单6.14 ExecutorCompletionService使用的QueueingFuture类 129
清单6.15 使用CompletionService渲染可用的页面元素 130
清单6.16 在预定时间内获取广告信息 132
清单6.17 在预定时间内请求旅游报价 134
清单7.1 使用volatile域保存取消状态 137
清单7.2 生成素数的程序运行一秒钟 137
清单7.3 不可靠的取消把生产者置于阻塞的操作中(不要这样做) 139
清单7.4 线程的中断方法 139
清单7.5 通过使用中断进行取消 141
清单7.6 向调用者传递InterruptedException 143
清单7.7 不可取消的任务在退出前保存中断 144
清单7.8 在外部线程中安排中断(不要这样做) 145
清单7.9 在一个专门的线程中中断任务 146
清单7.10 通过Future来取消任务 147
清单7.11 在Thread中,通过覆写interrupt来封装非标准取消 149
清单7.12 用newTaskFor封装任务中非标准取消 151
清单7.13 不支持关闭的生产者-消费者日志服务 152
清单7.14 向日志服务添加不可靠的关闭支持 153
清单7.15 向LogWriter添加可靠的取消 154
清单7.16 使用ExecutorService的日志服务 155
清单7.17 使用致命药丸来关闭 156
清单7.18 IndexingService的生产者线程 157
清单7.19 IndexingService的消费者线程 157
清单7.20 使用私有Executor,将它的寿命限定于一次方法调用中 158
清单7.21 关闭之后,ExecutorService获取被取消的任务 159
清单7.22 使用TrackingExecutorService为后续执行来保存未完成的任务 160
清单7.23 典型线程池的工作者线程的构建 162
清单7.24 UncaughtExceptionHandler接口 163
清单7.25 UncaughtExceptionHandler将异常写入日志 163
清单7.26 注册关闭钩子来停止日志服务 165
清单8.1 在单线程化的Executor中死锁的任务(不要这样做) 169
清单8.2 ThreadPoolExecutor通用的构造函数 172
清单8.3 创建一个可变长的线程池,使用受限队列和“调用者运行”饱和策略。 175
清单8.4 使用Semaphore来遏制任务的提交 176
清单8.5 ThreadFactory接口 176
清单8.6 定制的线程工厂 177
清单8.7 自定义的线程基类 178
清单8.8 修改一个标准工厂方法创建的Executor 179
清单8.9 扩展线程池以提供日志和计时功能 180
清单8.10 把顺序执行转换为并行执行 181
清单8.11 把顺序递归转换为并行递归 182
清单8.12 等待并行运算的结果 182
清单8.13 类似于“搬箱子”谜题的抽象 183
清单8.14 谜题解决者框架的链节点 184
清单8.15 顺序化的谜题解决者 185
清单8.16 并发版的谜题解决者 186
清单8.17 ConcurrentPuzzleSolver使用可携带结果的闭锁 187
清单8.18 能够感知任务不存在的解决者 188
清单9.1 使用Executor实现的SwingUtilities 193
清单9.2 构建于SwingUtilities之上的Executor 194
清单9.3 简单的事件监听器 194
清单9.4 将耗时任务绑定到可视化组件 196
清单9.5 提供用户反馈的耗时任务 196
清单9.6 取消耗时任务 197
清单9.7 支持取消、完成和进度通知的后台任务类 199
清单9.8 在BackgroundTask中启动一个耗时的、可取消的任务 200
清单10.1 简单的锁顺序死锁(不要这样做) 207
清单10.2 动态加锁顺序产生的死锁(不要这样做) 208
清单10.3 制定锁的顺序来避免死锁 209
清单10.4 开始一个循环,它在典型条件下制定死锁 210
清单10.5 协作对象问的锁顺序死锁(不要这样做) 212
清单10.6 使用开放调用来避免协作对象之间的死锁 214
清单10.7 发生死锁后线程转储的部分信息 217
清单11.1 串行访问任务队列 227
清单11.2 徒劳的同步(不要这样做) 230
清单11.3 锁省略的候选程序 231
清单11.4 持有锁超过必要的时间 233
清单11.5 减少锁持续的时间 234
清单11.6 应当分拆锁的候选程序 236
清单11.7 使用分拆的锁重构ServerStatus 236
清单11.8 基于哈希的map中使用分离锁 238
清单12.1 利用Semaphore实现的有限缓存 249
清单12.2 BoundedBuffer的基本单元测试 250
清单12.3 测试阻塞与响应中断 252
清单12.4 适用于测试的中等品质的随机数生成器 253
清单12.5 BoundedBuffer的生产者-消费者测试程序 255
清单12.6 PutTakeTest中的生产者和消费者类 256
清单12.7 测试资源泄漏 258
清单12.8 用于测试ThreadPoolExecutor的线程工厂 258
清单12.9 验证线程池扩展的测试方法 259
清单12.10 使用Thread.yield产生更多的交替操作 260
清单12.11 基于关卡的计时器 261
清单12.12 使用基于关卡的计时器进行测试 262
清单12.13 TimedPutTakeTest的驱动程序 262
清单13.1 Lock接口 277
清单13.2 使用ReentrantLock保护对象状态 278
清单13.3 使用tryLock避免锁顺序死锁 280
清单13.4 具有预定时间的锁 281
清单13.5 可中断的锁获取请求 281
清单13.6 ReadWriteLock接口 286
清单13.7 用读写锁包装的Map 288
清单14.1 状态依赖的可阻塞行为的结构 292
清单14.2 有限缓存不同实现的基类 293
清单14.3 如果有限缓存不满足先验条件,会停滞不前 294
清单14.4 调用GrumpyBoundedBuffer的客户端逻辑 294
清单14.5 有限缓存使用了拙劣的阻塞 296
清单14.6 有限缓存使用条件队列 298
清单14.7 状态依赖方法的规范式 301
清单14.8 在BoundedBuffer.put中使用“依据条件通知” 304
清单14.9 使用wait和notifyAll实现可重关闭的阀门 305
清单14.10 Conditon接口 307
清单14.11 有限缓存使用显式的条件变量 309
清单14.12 使用lock实现的计数信号量 310
清单14.13 AQS中获取和释放操作的规范式 312
清单14.14 二元闭锁使用AbstractQueuedSynchronizer 313
清单14.15 非公平的ReentrantLock中tryAcquire的实现 315
清单14.16 Semaphore的tryAcquireShared和tryAcquireShared方法 316
清单15.1 模拟CAS操作 322
清单15.2 使用CAS实现的非阻塞计数器 323
清单15.3 使用CAS避免多元的不变约束 326
清单15.4 使用ReentrantLock实现随机数字生成器 327
清单15.5 使用AtomicInteger实现随机数字生成器 327
清单15.6 使用Treiber算法(Treiber,1986)的非阻塞栈 331
清单15.7 Michael-Scott非阻塞队列算法中的插入(Michael与Scott,1996) 334
清单15.8 在ConcurrentLinkedQueue中使用原子化的域更新器 335
清单16.1 没有充分同步的程序可以产生令人惊讶的结果(不要这样做) 340
清单16.2 FutureTask的内部类示范了如何“驾驭”同步 343
清单16.3 不安全的惰性初始化(不要这样做) 345
清单16.4 线程安全的惰性初始化 347
清单16.5 主动初始化 347
清单16.6 惰性初始化holder类技巧 348
清单16.7 检查锁反模式(不要这样做) 349
清单16.8 不可变对象的初始化安全性 350