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模式来解决。
【实例查看上面参阅地址】
Two-phase Termination的更多相关文章
- Java:并发笔记-01
Java:并发笔记-01 说明:这是看了 bilibili 上 黑马程序员 的课程 java并发编程 后做的笔记 1. 进程与线程 本章内容 进程和线程的概念 并行和并发的概念 线程基本应用 1.1 ...
- 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 ...
- 数据库中的two phase locking
数据库中的two phase locking 两段锁协议是指每个事务的执行可以分为两个阶段:生长阶段(加锁阶段)和衰退阶段(解锁阶段). 加锁阶段:在该阶段可以进行加锁操作.在对任何数据进行读操作之前 ...
- 背水一战 Windows 10 (21) - 绑定: x:Bind 绑定, x:Bind 绑定之 x:Phase, 使用绑定过程中的一些技巧
[源码下载] 背水一战 Windows 10 (21) - 绑定: x:Bind 绑定, x:Bind 绑定之 x:Phase, 使用绑定过程中的一些技巧 作者:webabcd 介绍背水一战 Wind ...
- Xcode6 ADD Copy Files Build Phase 是灰色的
在学习的怎样写frameWork的时候,查看一个教程How to Create a Framework for iOS [一个中文翻译 创建自己的framework] 其中一个步骤就是添加一个Cop ...
- Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- 绑定: x:Bind 绑定, x:Bind 绑定之 x:Phase, 使用绑定过程中的一些技巧
背水一战 Windows 10 之 绑定 x:Bind 绑定 x:Bind 绑定之 x:Phase 使用绑定过程中的一些技巧 示例1.演示 x:Bind 绑定的相关知识点Bind/BindDemo.x ...
- A trip through the Graphics Pipeline 2011_09_Pixel processing – “join phase”
Welcome back! This post deals with the second half of pixel processing, the “join phase”. The pre ...
- A trip through the Graphics Pipeline 2011_08_Pixel processing – “fork phase”
In this part, I’ll be dealing with the first half of pixel processing: dispatch and actual pixel sha ...
- maven 错误No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format
[INFO] Scanning for projects... [INFO] ------------------------------------------------------------- ...
随机推荐
- win7 64位 TortoiseSVN-1.8.4客户端安装
下载地址链接:http://pan.baidu.com/s/1nukeBVz 密码:tc79 (32 64位都有,注意区分) next一路安装 安装好后,在需要和服务器同步的文件夹图标上--鼠标右键- ...
- android--email发送邮件,文本还有附件形式的邮件
1.首先用的jar包为javaemail 下载地址: https://yunpan.cn/cB3kY8WIvcGtU (提取码:e042) 2.工具包 package com.kllayhello.u ...
- MVC-06 安装部署
部署网站往往是一件麻烦事,因为在安装部署的过程中,经常有许多步骤要运行,对于许多不太熟悉IIS/SQL的新手来说,部署网站编程一件非常困难且危险的事.Visual Studio 2012在ASP.NE ...
- 用户管理_组管理_设置主机名_UGO_文件高级权限_ACL权限
用户管理: 添加用户:useradd tom 设置密码:passwd tom 切换账户: su - tom (不加-也能切换,但是 -会有两点不同 1.有-会切换到该用户的主目录 2.会切换到该用户 ...
- nginx 重写 rewrite 基础及实例(转)
nginx rewrite 正则表达式匹配 大小写匹配 ~ 为区分大小写匹配 ~* 为不区分大小写匹配 !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 文件及目录匹配 -f和!-f用来判断是否 ...
- WiFi密码破解CDlinux
好了,先说下提前要准备的东东吧:1.U盘一枚,最小1G空间.需进行格式化操作,提前保存内部文件.2.CDlinux镜像.帖子最后会提供一枚8月最新修改版,共135M. 1.CDlinux U盘启动 ...
- 【剑指offer】面试题43:n个骰子的点数
第一种思路是,每一个骰子的点数从最小到最大,如果为1-6,那么全部的骰子从最小1開始,我们如果一种从左向右的排列,右边的最低,索引从最低開始,推断和的情况. def setTo1(dices, sta ...
- Razor强类型视图下的文件上传
域模型Users.cs using System;using System.Collections.Generic;using System.Linq;using System.Web; namesp ...
- 加特殊符号星号斜杠反斜杠/* \ */ !important等让css实现兼容各个浏览器的技巧的代码
在编写css样式表的时候常常会碰到一写浏览器兼容的问题,象是不同内核的浏览器显示就不一定相同,不同版本的的浏览器也会产生上下兼容的问题,如何解决这些问题成了我们苦恼的问题,如果你对css hac ...
- 解决gnuplot中'Terminal type set to 'unknown'不能显示绘图的问题
安装gnuplot: sudo apt-get install gnuplot 安装成功后,在终端输入gnuplot,进入gnuplot. 直接进行一个小测试: plot sin(x) 发现不能显示绘 ...