Two-phase Termination

本文参阅【http://ifeve.com/java-two-phase-termination/】

Two-phase Termination模式简介

 停止线程是一个目标简单而实现却不那么简单的任务。首先,Java没有提供直接的API用于停止线程。此外,停止线程还有一些额外的细节需要考虑,如停止的线程处于阻塞(如等待锁)或者等待状态(等待其他线程),尚有未处理完的任务等。

  Two-phase Termination模式通过将停止线程这个动作分解为【准备阶段】和【执行阶段】这两个阶段,提供了一种通用的用于优雅地停止线程的方法。
准备阶段

  该阶段的主要动作是“通知”目标线程(欲停止的线程)准备进行停止。这一步会设置一个标志变量用于指示目标线程可与准备停止了。但是,由于目标线程可能正处于阻塞状态(等待锁的获得)、等待状态(如调用Object.wait)或者I/O(如InputStream.read)等待等状态,即便设置了这个标志,目标线程也无法立即”看到”这个标志而做出相应的动作。因此,这一阶段还需要用interrupt方法,以期望目标线程能够对能够通过捕获相关的异常侦测到该方法调用,从而中断其阻塞状态、等待状态。对于能够对interrupt方法调用做出响应的方法(参见表5-1),目标线程代码可以通过捕获这些方法抛出的InterruptedException来侦测线程停止信号。但也有一些方法(如InputStream.read)并不对interrupt调用作出响应,此时需要我们手工处理,如同步的Socket I/O操作中通过关闭socket,使处于I/O等待的socket抛出java.net.SocketException。

表1.能够对Thread.interrupt作出响应的一些方法

方法(或者类) 响应interrupt调用抛出的异常
Object.wait() ?Object.wait(long timeout) ?Object.wait
(long timeout, int nanos)
InterruptedException
Thread.sleep(long millis) ?Thread.sleep(long millis, int
nanos)
InterruptedException
Thread.join()?Thread.join(long millis) ?Thread.Join
(long millis, int nanos)
InterruptedException
java.util.concurrent.BlockingQueue.take() InterruptedException
java.util.concurrent.locks.Lock.lockInterruptibly() InterruptedException
java.nio.channels.InterruptibleChannel java.nio.channels.ClosedByInterruptException

执行阶段。该阶段的主要动作是检查准备阶段所设置的线程停止标志和信号,在此基础上决定线程停止的时机,并进行适当的清理操作。

Two-phase Termination 模式的架构

ThreadOwner:目标线程的拥有者.Java语言中,并没有线程拥有者的概念,但是线程的背后是其要处理的任务或者其所提供的服务,因此我们不能在不清楚某个线程具体是做什么的情况下贸然将其停止。一般地,我们可以将目标线程的创建者视为该线程的拥有者,并假定其”知道”目标线程的工作内容,可以安全地停止目标线程。
Terminatable:可停止线程的抽象。其主要方法及职责如下
  terminate:请求目标线程停止。
  AbstractTerminatableThread:可停止的线程。其主要方法及职责如下
  terminate:设置线程停止标志,并发送停止”信号”给目标线程。
  doTerminate:留给自己实现线程停止时所需的一些额外操作,如目标线程代码中包含SockerI/O,子类可以在该方法中关闭Socket以达到快速停止线程,而不会使目标线程等待I/O完成才能侦测到线程停止标记。
  doRun:线程处理逻辑方法。留给子类实现线程的处理逻辑。相当于Thread.run(),只不过该方法中无需关心停止线程的逻辑,因为这个逻辑已经被封装在TerminatableThread的run方法中了。
  doCleanup:留给子类实现线程停止后可能需要的一些清理动作。
TerminationToken:线程停止标志。toShutdown用于目标线程可以停止了。reservations可用于反映目标线程还有多少数量未完成的任务,以支持等目标线程处理完其他任务后再行停止。
ConcreteTerminatableThread:由应用自己实现的AbstractTerminatableThread参与者的实现类。该类需要实现其父类的doRun抽象方法,在其中实现线程的处理逻辑,并根据应用的实际需要覆盖(Override)其父类的doTerminate方法、doCleanup方法。

时序图:  

  第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方法会被调用。

Two-phase Termination模式实战案例解析

某系统的告警功能被封装在一个模块中。告警模块的入口类是AlarmMgr。其他模块(业务模块)需要发送告警信息时只需要调用AlarmMgr的sendAlarm方法即可。该方法将告警信息缓存如队列,由专门的告警发送线程负责调用AlarmAgent的相关方法发送告警。AlarmAgent类负责与告警服务器对接,它通过网络连接将告警信息发送至告警服务器。
告警发送线程是一个用户线程(user Thread),因此在系统的停止过程中,该线程若未停止则会组织JVM正常关闭。所以,在系统停止过程中我们必须主动去停止告警发送线程,而非依赖JVM。为了能够尽快地以优雅的方式将告警线程停止,我们需要处理以下两个问题。
1.当告警缓存队列非空时,需要将队列中已有的告警信息发送至告警服务器。
2.由于缓存告警信息的队列是一个阻塞队列(ArrayBlockingQueue),在该队列为空的情况下,告警发送线程会一直处于等待状态。这会导致其无法响应我们关闭线程的请求。
上述问题可以通过使用Two-phase Termination模式来解决。
【实例查看上面参阅地址

时间: 2024-08-01 10:41:41

Two-phase Termination的相关文章

JAVA多线程(十二)模式-Two Phase Termination

Two Phase Termination 显示调用资源清理方法,清理结束后正式关闭. 适用环境 需要在线程正式结束前清理资源. 样例 在线程的循环执行方法中检查终止状态,如果为真(或者运行中发生错误),退出循环体,进而执行循环体外finally部分进行清理. 此模式并非是立即停止,如果线程本身处于等待.阻塞等状态时,需要对中断后场景做处理,比如打断长时间等待的任务后转为继续处理其他任务. try{ while(!isStoped){ try{ doSomething(); }catch(Int

汪大神Java多线程编程实战

课程目录:├─1│  ├─Java并发编程.png│  ├─源码+ppt.rar│  ├─高并发编程第一阶段01讲.课程大纲及主要内容介绍.wmv│  ├─高并发编程第一阶段02讲.简单介绍什么是线程.wmv│  ├─高并发编程第一阶段03讲.创建并启动线程.mp4│  ├─高并发编程第一阶段04讲.线程生命周期以及start方法源码剖析.mp4│  ├─高并发编程第一阶段05讲.采用多线程方式模拟银行排队叫号.mp4│  ├─高并发编程第一阶段06讲.用Runnable接口将线程的逻辑执行单元

Java Concurrency - Phaser, Controlling phase change in concurrent phased tasks

The Phaser class provides a method that is executed each time the phaser changes the phase. It's the onAdvance() method. It receives two parameters: the number of the current phase and the number of registered participants; it returns a Boolean value

Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)

这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) Maven提高篇系列之(三)——使用自己Repository(Nexus) Maven提高篇系列之(四)——使用Profile Maven提高篇系列之(五)——处理依赖冲突 Maven提高篇系列之(六)——编写自己的Plugin(本系列完) 持续交付要“自动化所有东西”,对于集成测试也是一样.集成

Java多线程编程模式实战指南(三):Two-phase Termination模式--转载

本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-two-phase-termination.转载请注明作者: 黄文海 出处:http://viscent.iteye.com. 停止线程是一个目标简单而实现却不那么简单的任务.首先,Java没有提供直接的API用于停止线程.此外,停止线程时还有一些额外的细节需要考虑,如待停止的线程处于阻塞(等待锁)或者等待状态(等待其它

JSF教程(8)——生命周期之Apply Request Values Phase

当一个组件树在一个postbacks请求中被恢复之后其中每个组件从request的参数中取得各自的值,这里使用的是processDecodes方法.这个值会保存在本地的每个组件中,在源码中此过程的代码较为简单,因为实现者将取值这个操作全部封装在processDecodes方法中.不过说实话JSF的这部分的实现非常失败,因为在UIViewRoot中有将近两千行代码,这,这,这-- (UIViewRoot.java中的processDecodes方法) public void processDeco

Concurrent Request:Inactive phase,No Manager status

Symptom 随便submit一个request,发现几乎所有的Concurrent Manager都为No Manager状态,Phase为Inactive. Solution 一个Concurrent Request的生命周期包含不同的阶段(Phase):Pending, Running, and Completed,并且对于每一个Phase,又可能会有不同的状态. Pending Phase - Normal, Standby, Scheduled, Waiting Running Ph

loadrunner跑场景的时候出现:Abnormal termination, caused by mdrv process termination

目录[-] 1.问题 2.一些资料 2.1关于mdrv.exe意外终止的可能出现情况说明 2.2 mmdrv和mdrv的异同 2.3 Loadrunn中的多线程与多进程 3.解决方案 1.问题 loadrunner跑场景的时候出现:Abnormal termination, caused by mdrv process termination. 备注:我使用的是RTE协议录制的脚本. 2.一些资料 2.1关于mdrv.exe意外终止的可能出现情况说明 Hi, Users facing this

Java多线程编程模式实战指南(三):Two-phase Termination模式

停止线程是一个目标简单而实现却不那么简单的任务.首先,Java没有提供直接的API用于停止线程.此外,停止线程时还有一些额外的细节需要考虑,如待停止的线程处于阻塞(等待锁)或者等待状态(等待其它线程).尚有未处理完的任务等.本文介绍的Two-phase Termination模式提供了一种通用的用于优雅地停止线程的方法. Two-phase Termination模式简介 Java并没有提供直接的API用于停止线程.Two-phase Termination模式通过将停止线程这个动作分解为准备阶

[tgt]Login Phase

分析分析Login部分. iscsi协议的request和response都是成对出现的.也就是一个initiator发出一个reqestment,target就会回一个response.这种方式,从抓包结果,文档,或者tgt的实现里面对连接状态的,和event的处理上都可以看到.只要结合iscsi的这种行为模式,理解起来tgt的实现会容易很多(这里还是要感谢伍哥的点化……). 总体上来讲,iscsi在login阶段,request和response的序列是这样子的: ---Login init