修正Java中wait方法超时语意模糊性的一种方案
2008-01-05 19:20:56 来源:WEB开发网核心提示:java语言中内建了对于多线程的支持,可以非常方便的创建、控制线程以及在线程之间进行同步操作,修正Java中wait方法超时语意模糊性的一种方案,另外,为了支持更为高级的线程间同步机制,所以我们可以定义一个QueueFullCondition类继续自WaitWithTiming,在QueueFullCondition中
java语言中内建了对于多线程的支持,可以非常方便的创建、控制线程以及在线程之间进行同步操作。另外,为了支持更为高级的线程间同步机制,比如:类似于POSIX中的条件变量,Java在Object类中提供了wait、notify和notifyAll方法,使得所有的类都隐式的继续了这些方法。非凡地,为了提供对于程序健壮性方面的考虑,在Java中提供了对于wait方法超时语意的支持。但是Java在对于wait方法超时语意的支持方面存在模糊性,即在调用具有超时语意的wait方法返回时,无法区分是由于notify的通知还是由于超时触发的。因此应用开发者在构建需要具有超时语意的应用时,就必须负责对这种语意模糊性进行区分,本文将对这一问题进行剖析,并给出一个比较通用的解决方案。
问题提出
在作者所从事的项目中,要开发一个应用服务器,实现如下所述的功能:能够高效的处理来自多个客户端的并发请求。为了简化同步控制,分离并发逻辑和业务逻辑,我们采用了Active Object模式,具体的实现可以参见作者的另外一篇文章:《构建Java并发模型框架》(http://www-900.ibm.com/developerWorks/cn/java/l-multithreading/index.sHtml)。该设计中有一个核心部件ActiveQueue用于存放客户的请求。为了能够做到应用服务器的负载控制,我们对于ActiveQueue的大小进行了限制,假如当前的客户请求数量已经达到这个限制,就让后继的请求等待,具体的代码实现片断如下(为了简洁起见,省略了其他无关的代码):
该方法刚开始工作的很好,但是随着项目开发的进展,在随后的测试中我们发现了两个较为严重的问题:1、当并发请求的客户端很多时,会造成某些客户端等待的时间过长,对于客户端的使用者来说非常不友好;2、由于系统中应用服务器的其他方面的异常同样会造成客户端请求的永久等待比如:应用服务器在处理完客户端请求后,由于异常没有正确的调用相应notify方法。所以为了改善程序的用户友好性以及健壮性,我们决定采用带有超时语意的wait方法。该方法的原型声明如下:
可以看出,简单的使用一个具有超时语意的wait方法是不可行的,原因就在于wait方法超时语意的模糊性。在下面的小节会先给出一个初步的解决方案,随后我们将使用模式对于该方案进行重构从而构造出一个比较通用的方案。
初步解决方案
我们的初步解决方案采用了Doug Lea(Doug Lea为对象并发领域世界级的专家)给出的一个显式的判定算法(关于该算法更加具体、深入的论述请参考参考文献〔1〕),通过该算法来辨别是否已经超时。采用该算法的解决方案的代码实现片断如下:
可以看出,这个算法非常的简单,核心思路就是在每次wait返回时,计算wait等待的时间,并比较该时间和设定的要等待的时间,假如大于设定的要等待的时间,即确定为超时,否则确定为被notify唤醒。
使解决方案一般化
上述的解决方案针对我们目前的要求已经可以很好的工作了,但是细心的读者一定会发现在上面给出的解决方案中我们把两个无关的概念揉合在了一起:队列是否为满的判定逻辑和是否超时的计算判定逻辑。为什么说这是两个无关的概念呢?因为队列是否为满是与我们开发的具体应用相关的,不同的应用会有不同类型的判定逻辑(比如:不使用队列的应用可能会有其他在概念上类似的判定逻辑),而计算判定超时的逻辑是和具体应用无关的。假如我们能够把这两个概念剥离开来,那么这二者就可以独立变化,我们的解决方案的可重用性就会增强。
我们使用Template Method模式(参见参考文献〔2〕)来指导我们的重构。首先,我们会根据和具体应用无关的超时计算判定的算法定义一个通用的算法框架,把和具体应用逻辑相关的条件判定作为一个抽象的hook方法,延迟到具体的应用中去实现,从而实现了和应用无关的超时计算判定逻辑的复用。实现该算法的抽象基类的要害实现代码如下:
使用方法介绍
本小节我们将对上一节给出的抽象基类WaitWithTiming的使用方法进行具体的介绍。我们当然可以直接使得ActiveQueue继续自WaitWithTiming,并实现相应的抽象hook方法condition,但是这样做有一个弊端,就是对于ActiveQueue我们只能够实现仅仅一个condition,假如我们要添加针对dequeue时队列为空的条件判定逻辑就无能为力了,因为WaitWithWaiting仅仅只有一个condition方法(其实,即使有多个也没有办法做到通用,因为不能对具体的应用的需求进行假设)。
我们推荐的使用方法是,根据具体应用的需求,整理出需要的判定条件,创建相应的类来表示这些判定条件,使这些用来表示具体判定条件的类继续自WaitWithTiming,这些类中具体的条件判定逻辑的实现可以使用相应的具体的应用实体。比如:对于本文开始所列举的应用,我们需要的判定条件为队列为满,所以我们可以定义一个QueueFullCondition类继续自WaitWithTiming,在QueueFullCondition中实现抽象的hook方法condition的逻辑,在该逻辑中在使用ActiveQueue的isFull方法。
更多精彩
赞助商链接