避免Java线程处理的陷阱

ZDNet软件频道 时间:2003-03-11 作者:周靖 译 |  我要评论()
本文关键词:
如果Java是程序员学习的第一种语言,线程问题就可能成为一个难以逾越的障碍。本文探讨了进行线程处理时,一些经常遇到的陷阱。
本文译自Builder.com程序开发语言的线程处理并不是什么新思想。并发执行是任何多任务环境的基础。但是,Java要求开发者使用显式的线程处理来完成一些最基本的任务,比如使用非阻塞I/O来进行读取。正是因为即使最简单的功能也要依赖线程处理,所以开发者在语言的学习阶段就不得不和Java线程打交道。令人遗憾的是,如果Java是他们学习的第一种语言,线程问题就可能成为一个难以逾越的障碍。本文探讨了进行线程处理时,一些经常遇到的陷阱。
未同步的wait()

这是每个人在使用wait时第一次遇到的陷阱。清单A展示了如何对一个随意的对象发出wait调用。在wait调用之前,代码能够很好地编译和运行。之后,它会引发一个IllegalMonitorStateException异常。对一个对象调用wait之前,首先要将wait调用封闭到一个同步的块中,从而获得对象的监视器。清单B提供了这样的一个例子。幸运的是,这个陷阱很容易避免;因为有现成的异常可供捕捉。但是,线程处理所牵涉的大多数错误都没有这么“体贴”,它们会间歇性地发生,或者悄悄地死锁。下面来看看几个例子。

未同步的退出条件

我们经常将线程用于一个循环的封送(dispatch)或处理器(processor)线程。这些线程无休止地循环,等待一个外部线程改变可相互访问的一个对象的状态。但是,如果不小心地同步两个线程所访问的对象,就可能不知不觉发生死锁。在清单C中,一个处理器线程不停循环,直到Boolean变量done被设为true。程序作者明显是想让一个外部线程将done标志切换为true,并在当前循环结束之后就退出循环。

然而,Boolean变量done的状态可能不慎越过线程边界。Java内存模型并不保证在一个线程中对done进行的改动会在另一个线程中反映出来。所以,可能有一个外部线程改换了这个标志的状态,而其他线程中的while循环无休止地进行。老练的开发者也许会说,他们以前用的就是像清单C那样的代码,而且都能很好地工作。这并没有错;它通常确实能很好地工作。但是,并不保证它总是像预期的那样工作,而且每个有经验的开发者都应知道适用于重要事件及间歇性错误的“摩非定律”(会出错的终将出错——译注)。

Java内存模型要求:遇到一个同步障碍时,变量状态是正确的。这可消除处理器线程无限循环的风险,如清单D所示。“博学”的Java开发者可能会问,能否用volatile关键字来纠正相同的问题?volatile关键字确实是为这种情况而设的,它的宗旨是:如果变量已在另一个线程中更新,那么在访问过时数据时,就强迫进行内存读取。遗憾的是,Java语言规范在解释volatile的工作原理时语焉不详,许多虚拟机甚至完全忽略了这个关键字。


百度大联盟认证黄金会员Copyright© 1997- CNET Networks 版权所有。 ZDNet 是CNET Networks公司注册服务商标。
中华人民共和国电信与信息服务业务经营许可证编号:京ICP证010391号 京ICP备09041801号-159
京公网安备:1101082134