5.2 Two-phase Termination模式的架构
Two-phase Termination模式的主要参与者有以下几种,其类图如图5-1所示。
图5-1 Two-phase Termination模式的类图
• ThreadOwner:目标线程的拥有者。在Java语言中,并没有线程拥有者的概念,但是线程的背后是其要处理的任务或者其所提供的服务,因此我们不能在不清楚某个线程具体要做什么的情况下贸然将其停止。一般地,我们可以将目标线程的创建者视为该线程的拥有者,并假定其“知道”目标线程的工作内容,以及可以安全地停止目标线程。
• Terminatable:可停止线程的抽象,其主要方法及职责如下。
➢ terminate:请求目标线程停止。
• AbstractTerminatableThread:可停止的线程,其主要方法及职责如下。
➢ terminate:设置线程停止标志,并发送停止“信号”给目标线程。
➢ doTerminate:留给子类实现的线程停止时所需的一些额外操作,如目标线程代码中包含Socket I/O,子类可以在该方法中关闭Socket以快速停止线程,而不会使目标线程等待I/O完成后才能侦测到线程停止标志。
➢ doRun:线程处理逻辑方法,留给子类实现的线程的处理逻辑。相当于Thread.run,只不过在该方法中无须关心停止线程的逻辑,因为这个逻辑已经被封装在AbstractTerminatableThread的run方法中了。
➢ doCleanup:留给子类实现的线程停止后可能需要的一些清理动作。
• TerminationToken:线程停止标志。toShutdown用于指示目标线程可以停止了。reservations可用于反映目标线程还有多少未完成的任务,以实现等待目标线程处理完其任务后再行停止。
• ConcreteTerminatableThread:由应用自己实现的AbstractTerminatableThread参与者的实现类。该类需要实现其父类的doRun抽象方法,在其中实现线程的处理逻辑,并根据应用的实际需要覆盖(Override)其父类的doTerminate方法、doCleanup方法。
准备阶段的序列图如图5-2所示。
图5-2 准备阶段的序列图
第1步:客户端代码调用线程拥有者的shutdown方法。
第2步:shutdown方法调用目标线程的terminate方法。
第3、4步:terminate方法将terminationToken的toShutdown标志变量设置为true。
第5步:terminate方法调用由AbstractTerminatableThread子类实现的doTerminate方法,使得子类可以为停止目标线程做一些其他必要的操作。
第6步:若terminationToken的reservations属性值为0,则表示目标线程没有未处理完毕的任务或者ThreadOwner在停止线程时不关心其是否有未处理完毕的任务。此时,terminate方法会调用目标线程的interrupt方法。
第7步:terminate方法调用结束。
第8步:shutdown方法调用返回,此时目标线程可能仍然在运行。
执行阶段由目标线程的run方法来检查terminationToken的toShutdown属性、reservations属性的值,并捕获由interrupt方法调用抛出的相关异常,以决定是否停止线程。在线程停止前,由AbstractTerminatableThread子类实现的doCleanup方法会被调用。