随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序取得了显著的进步,提高了Java虚拟机的性能,提高了并发类的可伸缩性,并加入了丰富的新并发构建块。在本书中,这些便利工具的创造者不仅解释了它们究竟如何工作、如何使用,同时,还阐释了创造它们的原因,及其背后的设计模式。 本书既能够成为读者的理论支持,又可以作为构建可靠的,可伸缩的,可维护的并发程序的技术支持。本书并不仅仅提供并发API的清单及其机制,本书还提供了设计原则,模式和思想模型,使我们能够更好地构建正确的,性能良好的并发程序。\r\n 本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5,6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。
代码清单\r\n序\r\n第1章 介绍\r\n 1.1 并发的(非常)简短历史\r\n 1.2 线程的优点\r\n 1.3 线程的风险\r\n 1.4 线程无处不在\r\n第1部分 基础\r\n 第2章 线程安全\r\n 2.1 什么是线程安全性\r\n 2.2 原子性\r\n 2.3 锁\r\n 2.4 用锁来保护状态\r\n 2.5 活跃度与性能\r\n 第3章 共享对象\r\n 3.1 可见性\r\n 3.2 发布和逸出\r\n 3.3 线程封闭\r\n 3.4 不可变性\r\n 3.5 安全发布\r\n 第4章 组合对象\r\n 4.1 设计线程安全的类\r\n 4.2 实例限制\r\n 4.3 委托线程安全\r\n 4.4 向已有的线程安全类添加功能\r\n 4.5 同步策略的文档化\r\n 第5章 构建块\r\n 5.1 同步容器\r\n 5.2 发容器\r\n 5.3 阻塞队列和生产者一消费者模式\r\n 5.4 阻塞和可中断的方法\r\n 5.5 Synchronizer\r\n 5.6 为计算结果建立高效、可伸缩的高速缓存\r\n第2部分 构建并发应用程序\r\n 第6章 任务执行\r\n 6.1 在线程中执行任务\r\n 6.2 Executor 框架\r\n 6.3 寻找可强化的并行性\r\n 第7章 取消和关闭\r\n 7.1 任务取消\r\n 7.2 停止基于线程的服务\r\n 7.3 处理反常的线程终止\r\n 7.4 JVM关闭\r\n 第8章 应用线程池\r\n 8.1 任务与执行策略问的隐性耦合\r\n 8.2 定制线程池的大小\r\n 8.3 配置ThreadPoolExecutor\r\n 8.4 扩展ThreadPoolExecutor\r\n 8.5 并行递归算法\r\n 第9章 GUI应用程序\r\n 9.1 为什么GUI是单线程化的\r\n 9.2 短期的GUI任务\r\n 9.3 耗时GUI任务\r\n 9.4 共享数据模型\r\n 9.5 其他形式的单线程子系统\r\n第3部分 活跃度,性能和测试\r\n 第10章 避免活跃度危险\r\n 第11章 性能和可伸缩性\r\n 第12章 测试并发程序\r\n第4部分 高级主题\r\n 第13章 显示锁\r\n 第14章 构建自定义的同步工具\r\n 第15章 原子变量与非阻塞同步机制\r\n 第16章 Java存储模型\r\n附录A 同步Annotation\r\n参考文献\r\n索引
本书作者系lava标准化组织(Java Cotl]munity Process)JSR 166专家组(并发工具)的主要成员,同时他们还致力于其他多个JCP专家组织。Brain Goetz是一位拥有二十年行业经验的软件咨询师,发表过超过75篇关于。Java开发的文章。Tim Peierls是现代多处理器的权威,在BoxPop.biz、唱片艺术和戏剧表演上也造诣颇深。Joseph Bowbeer是一位Java ME专家,他对并发编程的痴迷始于在Apollo计算机上编程的岁月。David Holmes是《The Java TM Programmjng LangLJage》的合著者,目前就职于Sun Microsysterns。Joshua Bloch是Google的首席Java架构师,《Effective Java》的作者、《3ava PLizzlers》的合著者,他不像他的兄弟(his brother,Bloctl与Neal主持的Java编程专栏里虚构的人物)那样编程,从来都不。Doug Lea是《Corlcurrent Progra r11ming in Java》的作者,SUNY Oswego大学计算机科学的教授。
写作本书时,出于桌面系统的迫切需求,多核处理器正在变得越来越便宜。与此不协调的是,很多开发团队还没有注意到,在他们的项目中,出现了越来越多的关于线程的错误报告。在NetBeans开发者站点上最近的一次通告中,一位核心维护者注意到,为了修复某个类的一个线程相关的问题,已经被打了14次补丁。Dion Almaer,前TheServerSide的编辑(经过一次痛苦的调试过程,最终发现是一个线程的Bug之后),最近在Blog上写道,大多数Java程序都充斥着并发Bug,它们仅仅是“碰巧”可以工作。
的确,由于并发性的Bug不会以可预见的方式自己“蹦”出来,因此多线程程序的开发、测试和调试都会变得极端困难。Bug浮出水面的时刻,通常可能是最坏的时候——对应于生产环境,就是指在高负载的时候。
使用Java开发并发程序所要面对的挑战之一,是要面对平台提供的各种并发特性之间的不匹配,还有就是程序员在他们的程序中需要如何思考并发性。语言提供了一些低层机制,比如同步和条件等待,但是这些机制在实现应用级的协议与策略时才是必须的。不顾这些策略的约束,很容易创建出一个程序,它在编译和运行时看上去一切正常,不过却存在隐患。很多并发方面相当不错的书都没能达到预期的目标,它们过分关注于低层的机制和API,而不是设计层面的策略和模式。
Java 5.0是在使用Java开发并发应用程序的进程中,迈出的巨大一步。它提供了新的高层组件以及更多的低层机制,这些将使得一名新手更容易像专家那样去构建并发应用程序。本书的作者都是JCP专家组的主要成员,正是这个专家组创建了这些新工具;除了去描述新工具的行为和特性,我们还向您展示了它们低层的设计模式,预期的使用场景以及将它们纳入平台核心库的动机。
我们的目标是给读者一些设计法则和理念模型,让读者在使用Java构建正确、高效的并发类和应用程序时,变得更容易——更有趣。
我们希望你在阅读《Java并发编程实践》的过程中能够获得愉悦感。
Brian Goetz
2006年3月于Williston, VT
如何使用本书
Java低层机制与设计层必要的策略之间存在着抽象的不匹配。为了解决这个问题,我们呈现了一个经过简化的规则集,用来编写并发程序。专家们看到这些规则会说:“嗨,这可不是完整的规则,类C即使破坏了规则R,它仍然可以是线程安全的。”尽管打破我们的规则,也可能写出正确的并发程序,不过这样做需要对Java存储模型的低层细节有着深入的理解。我们的愿望是:开发者不用熟悉这些细节,就有能力编写正确的并发程序。只要坚持遵守我们简单的规则,就能编写出正确的、可维护的并发程序。
我们假设读者已经具备了对Java基本并发机制的一些了解。《Java并发编程实践》不是并发的入门指南——关于这个,可以参看任何正统的介绍性的大部头书籍,比如《Java编程语言》(Arnold et al., 2005)。本书也不是关于并发的百科全书似的参考手册——关于这个,可以参看《Java并发编程(Lea, 2000)》。对本书更恰当的描述是,它提供了实际的设计规则,可以协助开发者,他们正在处于创建安全的、高效的并发类这一艰难的过程中。在适当的地方,我们穿插引用了下列图书的章节:《The Java Programming Language》、《Concurrent Programming in Java》、《The Java Language Specification (Gosling et al., 2005)》以及《Effective Java(Bloch, 2001)》,并使用[JPL n.m]、[CPJ n.m]、[JLS n.m]和[EJ Item n]来表示它们。
结束 “介绍”(第1章)之后,本书分为4部分:
基础。第一部分(第2~5章)关注于同步和线程安全的基本概念,以及如何使用类库提供的构建块组合线程安全类。110页上,有一个“并发诀窍清单”总结了出现在第一部分中最重要的规则。
第2章(线程安全性)与第3章(共享对象)构成了全书的基础。几乎所有用来避免并发危险、创建线程安全类以及验证线程安全的规则都包括在这里。轻“理论”,重“实践”的读者可能会禁不住诱惑跳过这部分,而直接进入第二部分,但是在开始编写任何并发代码之前,一定要确保回过头来阅读这两章!第4章所涵盖的技术,用于把线程安全类组合到更大的线程安全类中。第5章(构建块)涵盖了平台核心库提供的并发构建块——线程安全的容器和同步工具(synchronizer)。
构建并发应用程序。第二部分(第6~9章)描述了如何利用线程提高并发应用程序的吞吐量或响应性。第6章(任务执行)讲述如何识别可并行执行的任务,并在任务执行框架内部执行它们。第7章讲到的技术可以让任务和线程在正常终止之前妥善地终止;区分并发应用程序是健壮的,还是仅仅可以将就工作的,有众多的因素,“程序如何处理取消与关闭”就是其中之一。第8章(应用线程池)关注了一些任务执行框架中更为高级的特性。
第9章(GUI应用程序)关注了用来提高单线程化子系统响应性的技术。
活跃度、性能和测试。第三部分(第10~12章)涉及并发程序自身。要确保并发程序执行了你所希望它做的事情,而且性能是可以接受的。第10章(避免活跃度危险)描述了如何避免活跃度失败,活跃度失败会阻止程序继续向前执行。第11章(性能和可伸缩性)涵盖的技术用来提高并发代码的性能和可伸缩性。第12章(测试并发程序)涵盖的技术用来测试并发代码的正确性和性能。
高级主题。第四部分(第13~16章)涵盖的主题可能只会引起资深程序员的兴趣:它们是显式锁、原子变量、非阻塞算法和开发自定义的synchronizer。
代码示例
尽管书中很多的通用概念适用于Java 5.0之前版本,甚至是非Java环境,不过大多数示例代码(以及关于Java存储模型的每一句话)都假定是以Java 5.0或更新的JDK为基础的。有些代码示例还会用到Java 6中添加到类库中的特性。
代码示例已经被裁减,以降低它们的尺寸和突出相关的部分。完整版本的代码示例、辅助示例和勘误表,可以从本书的网站http://www.javaconcurrencyinpractice.com上获得。
代码示例分为三类:“好”示例,“一般”示例和“坏”示例。“好”示例阐释的技术应该被效仿。“坏”示例阐释的技术绝对不应该被效仿,而且还会用一个“Mr. Yuk”1的图标清楚地表明这是“有害”的代码(参见清单1)。“一般”示例阐释的技术不一定是错的,但却是是脆弱的、有风险的或者执行效果差的,而且会用一个“Mr. CouldBeHappier”图标标识出来,如同清单2。
清单1 糟糕的清单排序方法(不要这样做)
public > void sort(List list) {
// 永远不要返回错误的答案
System.exit(0);
}
有些读者会质疑“坏”示例在本书中的角色;毕竟,一本书应该展现如何做正确的事,而不是错误的事。“坏”示例有两个目的。它们揭示了常见的缺陷,更重要的是它们示范了如何分析程序的线程安全性——完成此事的最佳办法就是观察威胁线程安全的各种方式。
清单2 缺少优化的清单排序方法
public > void sort(List list) {
for (int i=0; i<1000000; i++)
doNothing();
Collections.sort(list);
}
致谢
java.util.concurrent包的开发过程催生了本书。这个包是由Java Community Process JSR 166创建的,后被加入到Java 5.0中。还有很多人为JSR 166作出过贡献;我们尤其要感谢Martin Buchholz,他完成了所有把代码植入JDK的相关工作;还要感谢concurrency-interest邮件清单的所有读者,他们为API的草案提供了他们的建议和反馈。
本书能够大幅度地完善,得益于大量的建议和帮助,这些帮助来自多方面的人员。我们要感谢Dion Almaer,Tracy Bialik,Cindy Bloch,Martin Buchholz,Paul Christmann,Cliff Click,Stuart Halloway,David Hovemeyer,Jason Hunter,Michael Hunter,Jeremy Hylton,Heinz Kabutz,Robert Kuhar,Ramnivas Laddad,Jared Levy,Nicole Lewis,Victor Luchangco,Jeremy Manson,Paul Martin,Berna Massingill,Michael Maurer,Ted Neward,Kirk Pepperdine,Bill Pugh,Sam Pullara,Russ Rufer,Bill Scherer,Jeffrey Siegal,Bruce Tate,Gil Tene,Paul Tyma,以及硅谷模式小组的成员,他们通过很多饶有趣味的技术交流,提供了指南,提出了建议,这些对本书能够做到更好颇有帮助。
我们格外感激Cliff Biffle,Barry Hayes,Dawid Kurzyniec,Angelika Lan
无封面