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

4.3 Guarded Suspension模式实战案例解析

某系统有一个告警[2]功能模块。该模块的主要功能是将其接收到的告警信息发送给告警服务器。该模块中的类AlarmAgent负责与告警服务器进行对接。AlarmAgent的sendAlarm方法负责通过网络连接(Socket连接)将告警信息发送给告警服务器。AlarmAgent创建了一个专门的线程用于其与告警服务器建立网络连接。因此,在sendAlarm方法被调用的时候,连接线程可能还没有完成网络连接的建立。此时,sendAlarm方法应该等待连接线程建立好网络连接。另外,即便连接线程建立好了网络连接,中途也可能由于某些原因出现与告警服务器断连的情况。此时,sendAlarm方法需要等待心跳(Heartbeat)任务[3]重新建立好连接后,才能上报告警信息。也就是说,sendAlarm方法必须在AlarmAgent与告警服务器的网络连接建立成功的情况下才能执行其所要执行的操作。若AlarmAgent与告警服务器的连接未建立(或者连接中断),sendAlarm方法的执行线程应该被暂挂直到连接建立完毕(或者恢复)。

上述问题可以应用Guarded Suspension模式来解决,如清单4-1所示。

清单4-1 AlarmAgent类源代码

如清单4-1所示代码中的sendAlarm方法,会等待其所属的AlarmAgent实例连接上告警服务器后才调用doSendAlarm方法来真正地将告警信息上报给告警服务器。若在sendAlarm方法被调用时AlarmAgent实例与告警服务器的连接未建立完毕或者连接中断了,则sendAlarm方法会使当前线程阻塞,直到其他线程唤醒该被阻塞的线程且AlarmAgent实例与告警服务器的连接建立完毕。可见,AlarmAgent实例相当于Guarded Suspension模式中的GuardedObject参与者实例,其sendAlarm方法是一个受保护方法。sendAlarm方法的目标动作是doSendAlarm所执行的操作(上报告警信息给告警服务器)。sendAlarm方法的保护条件是AlarmAgent实例与告警服务器已建立网络连接(即connectedToServer的值为true)。

AlarmAgent的实例变量agentConnected相当于Predicate参与者实例。AlarmAgent的实例变量blocker相当于Blocker参与者实例。

AlarmAgent的onConnected方法相当于GuardedObject参与者的stateChanged方法。当告警服务器连接线程建立起与告警服务器的连接,或者心跳定时任务在网络连接中断后重新连接完毕时,onConnected方法都会被调用。此时,onConnected方法通过调用Blocker的实例blocker的signalAfter方法[4],将标志变量connectedToServer设置为true,并“通知”blocker去唤醒被暂挂的sendAlarm方法去执行线程。

清单4-1中的代码所引用的其他类的源代码,如清单4-2、清单4-3、清单4-4和清单4-5所示。

清单4-2 Predicate接口源代码

清单4-3 GuardedAction类源代码

清单4-4 Blocker接口源代码

清单4-5 ConditionVarBlocker类源代码