Java理论和实践: 安全构造技术 - 编程入门网
Java理论和实践: 安全构造技术时间:2011-02-04 Brian GoetzJava 语言提供了灵活的、看上去很简单的线程功能,使得您很容易在您的应用程序中使用多线程。然而,Java应用程序中的并发编程比看上去要复杂:在 Java 程序中,有一些微妙(也许并不是那么微妙)方式会造成数据争用(data race)以及并发问题。在这篇 Java 理论和实践中,Brian探讨了一个常见的线程方面的危险:在构造过程中,允许 this 引用逃脱(escape)。这个看上去没有什么危害的做法可以在 Java 程序中造成无法可预料和不期望的结果。 测试和调试多线程程序是极其困难的,因为并发性方面的危险常常不是以一致的方式显现出来,甚至有时未必会显现这种危险性。就线程问题的本质而言,大多数这些问题是无法预料的,甚至在某些平台上(如单处理器系统),或者低于一定的负载,问题可能根本就不出现。由于测试多线程程序的正确性是如此困难,以及查找错误是如此费时,因此从一开始开发应用程序就要在心中牢记线程的安全性,这一点就显得尤为重要。在本文中,我们将研究一个特殊的线程安全方面的问题 ― 在构造过程中,允许 this 引用逃脱(我们称之为 逃脱的引用问题) ― 该问题引起了一些未曾期望的结果。然后,为了编写出线程安全的构造函数,我们给出一些准则。 遵循“安全构造”技术 分析程序来找出线程安全的违例是非常困难的,这需要专门的经验。幸运的是(也许会感到吃惊),从一开始创建线程安全的类并不是那样的困难,尽管这需要一种其它专门的技巧:规程。大多数并发性错误是来自程序员以方便、改善性能或只是一时的懒惰为名企图违规而造成的。如许多其它并发性问题一样,在编写构造函数时,遵循一些简单的规则就可以避免这个逃脱的引用问题。 危险的争用状态 大多数并发性危险归根结底是由某类 数据争用引起的。在多个线程或进程正在读取和写入一个共享数据项时,会发生数据争用或进入 争用状态 ,最终结果取决于这些线程的调度次序。清单 1 给出了一个简单的数据争用的示例,其中程序可以打印 0 或者 1,这取决于对线程的调度。 清单 1. 简单的数据争用
可以立即调度第二个线程,打印 a 的初始值 0。另一种情形,第二个线程可能 不立即运行,则导致打印值 1。这个程序的输出取决于您正在使用的 JDK、底层操作系统的调度程序或者随机计时构件。重复运行该程序,会得到不同的结果。 可见性危险 在清单 1 中,除了这个明显的争用 ― 第二个线程是在第一个线程将 a 置为 1 之前还是之后开始执行 ― 之外,实际上还有另一种数据争用。第二种争用是一种可见性方面的争用:两个线程没有使用同步,而同步能保证线程之间数据更改的可见性。因为没有同步,如果在第一个线程对 a 赋值完成之后,运行第二个线程,则第二个线程可能或 不可能立即看见第一个线程所做的更改。第二个线程可能看到 a 仍然为 0,即使第一个线程已经将值 1 赋给了 a。这种第二类的数据争用(在没有正确同步的情况下,两个线程正在访问同一变量)是一种复杂的问题,但幸运的是,每当读取一个其它线程可能已写过的变量,或者写一个接下来可能会被其它线程读取的变量时,使用同步就可以避免这类数据争用。在这里,我们不想进一步探讨这类数据争用,关于这类复杂问题,您可以参阅侧栏 “用 Java Memory Model 同步”,也可以参阅 参考资料以获取更多有关这类复杂问题的详 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |