Java多线程编程实战指南:设计模式篇(第2版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

4.2 Guarded Suspension模式的架构

Guarded Suspension模式的核心是一个受保护方法(Guarded Method)。该方法在执行其所要真正执行的操作时需要满足特定的条件(Predicate,以下称之为保护条件)。当该条件不满足时,执行受保护方法的线程会被挂起并进入等待(WAITING)状态,直到该条件满足,该线程才会继续运行。此时,受保护方法才会真正执行其所要执行的操作。为方便起见,以下称受保护方法所要真正执行的操作为目标动作。

Guarded Suspension模式的主要参与者有以下几种,其类图如图4-1所示。

图4-1 Guarded Suspension模式的类图

• GuardedObject:包含受保护方法的对象,其主要方法及职责如下。

➢ guardedMethod:受保护方法。

➢ stateChanged:改变GuardedObject实例状态的方法。该方法负责在保护条件成立时唤醒受保护方法的执行线程。

• GuardedAction:抽象了目标动作,并关联了目标动作所需的保护条件,其主要方法及职责如下。

➢ call:用于表示目标动作的方法。

• ConcreteGuardedAction:应用程序所实现的具体目标动作及其关联的保护条件。

• Predicate:抽象了保护条件,其主要方法及职责如下。

➢ evaluate:用于表示保护条件的方法。

• ConcretePredicate:应用程序所实现的具体保护条件。

• Blocker:负责对执行guardedMethod方法的线程进行挂起和唤醒,并执行ConcreteGuardedAction所实现的目标动作,其主要方法及职责如下。

➢ callWithGuard:负责执行目标动作和暂挂当前线程。

➢ signalAfter:负责执行其参数指定的动作和唤醒由该方法所属Blocker实例所暂挂的线程中的一个线程。

➢ broadcastAfter:负责执行其参数指定的动作和唤醒由该方法所属Blocker实例所暂挂的所有线程。

➢ signal:负责唤醒由该方法所属Blocker实例所暂挂的线程中的一个线程。

➢ broadcast:负责唤醒由该方法所属Blocker实例暂挂的所有线程。

• ConditionVarBlocker:基于Java条件变量(java.util.concurrent.locks.Condition)实现的Blocker。

受保护方法的内部处理逻辑序列图如图4-2所示。

图4-2 受保护方法的内部处理逻辑序列图

第1步:客户端代码调用受保护方法guardedMethod。

第2步:guardedMethod方法创建GuardedAction的实例guardedAction。

第3步:guardedMethod方法以guardedAction为参数调用Blocker实例的callWithGuard方法。

第4、5步:callWithGuard方法调用guardedAction的getGuard方法获取保护条件predicate。

第6~8步:这几个步骤是一个循环。该循环会判断保护条件是否成立(第6、7步)。若保护条件成立,则退出该循环;否则,该循环会将当前线程暂挂使其处于等待(WAITING)状态(第8步)。在其他线程唤醒该被暂挂的线程后(即第8步的方法调用返回后),该循环仍然继续检测保护条件,并重复上述逻辑。

第9、10步:callWithGuard方法调用guardedAction的call方法来执行目标动作,并记录call方法的返回值actionReturnValue。

第11步:callWithGuard方法将actionReturnValue作为其返回值返回给调用方。

第12步:guardedMethod方法返回。

在受保护方法的执行线程被暂挂后,当保护条件成立时,其他线程需要唤醒该线程,序列图如图4-3所示。

图4-3 被暂挂线程的唤醒序列图

第1步:客户端代码调用stateChanged方法改变GuardedObject实例的状态。这些状态包含了保护条件所关心的状态。

第2步:stateChanged方法创建java.util.concurrent.Callable的实例stateOperation。stateOperation封装了改变GuardedObject实例状态所需的操作。

第3步:stateChanged方法以stateOperation为参数,调用Blocker实例的signalAfter方法。

第4、5步:signalAfter方法调用stateOperation对象的call方法以改变GuardedObject实例的状态,并记录其返回值shouldSignalBlocker。

第6、7步:signalAfter方法在shouldSignalBlocker值为true时,调用java.util.concurrent. locks.Condition实例的signal方法来唤醒被该Condition实例所暂挂的线程中的一个线程。

第8步:signalAfter方法返回。此时,执行受保护方法的线程可能已经被唤醒(取决于shouldSignalBlocker的值是否为true)。

第9步:stateChanged方法返回。