Java多线程 两阶段终止模式Two-Phase Termination Patter
作者:冬日毛毛雨 发布时间:2023-11-29 04:47:04
目录
1、两阶段终止模式介绍
2、Terminator代码演示
3、TerminationRequester
4、模拟客户端或者服务端都可能终止服务的例子
5、mac telnet模拟客户端输入
1、两阶段终止模式介绍
有时候,我们希望提前结束线程,但安全可靠地停止线程,并不是一件容易的事情,如果立即停止线程,会使共享的数据结构处于不一致的状态,如目前已经废弃使用的Thread类的stop方法(它会使线程在抛出java.lang.ThreadDeath
之后终止线程,即使是在执行synchronized
方法的时候)。更好的做法是执行完终止处理,再终止线程,即Two-phase Termination
,两阶段终止模式。
该模式有两个角色:
Terminator
,终止者,负责接收终止请求,执行终止处理,处理完成后再终止自己。TerminationRequester
:终止请求发出者,用来向Terminator
发出终止请求。
2、Terminator代码演示
该模式示例代码如下:
public class CounterIncrement extends Thread {
private volatile boolean terminated = false;
private int counter = 0;
private Random random = new Random(System.currentTimeMillis());
@Override
public void run() {
try {
while (!terminated) {
System.out.println(Thread.currentThread().getName()+" "+counter++);
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
this.clean();
}
}
private void clean() {
System.out.println("do some clean work for the second phase,current counter "+counter);
}
public void close() {
this.terminated = true;
this.interrupt();
}
}
3、TerminationRequester
public class CounterTest {
public static void main(String[] args) throws InterruptedException {
CounterIncrement counterIncrement = new CounterIncrement();
counterIncrement.start();
Thread.sleep(15_000L);
//主动清理
counterIncrement.close();
}
}
这段代码可以看出实现两阶段终止模式必须注意的是:
使用线程停止标志和interrupt
方法,两者缺一不可
public void close() {
this.terminated = true;
this.interrupt();
}
这里使用了terminated
作为线程停止标志,变量采用volatile
修饰,避免了使用显式锁的开销,又保证了内存可见性。线程run
方法会检查terminated
属性,如果属性为true
,就停止线程,但线程可能调用了阻塞方法,处于wait
状态,任务也就可能永远不会检查terminated
标志;线程也有可能处于sleep()
状态,等sleep
时间过后再执行终止状态,程序的响应性就下降了。你可以把方法改成如下运行,线程停止明显变慢了许多:
public void close() {
terminated = true;
}
4、模拟客户端或者服务端都可能终止服务的例子
public class AppServer extends Thread {
private static final int DEFAULT_PORT = 12722;
private final static ExecutorService executor = Executors.newFixedThreadPool(10);
private int port;
private volatile boolean start = true;
private List<ClientHandler> clientHandlers = new ArrayList<>();
private ServerSocket server;
public AppServer() {
this(DEFAULT_PORT);
}
public AppServer(int port) {
this.port = port;
}
@Override
public void run() {
try {
server = new ServerSocket(port);
while (start) {
Socket client = server.accept();
ClientHandler clientHandler = new ClientHandler(client);
executor.submit(clientHandler);
this.clientHandlers.add(clientHandler);
}
} catch (IOException e) {
//throw new RuntimeException();
} finally {
this.dispose();
}
}
public void dispose() {
System.out.println("dispose");
this.clientHandlers.stream().forEach(ClientHandler::stop);
this.executor.shutdown();
}
public void shutdown() throws IOException {
this.start = false;
this.interrupt();
this.server.close();
}
}
public class ClientHandler implements Runnable {
private final Socket socket;
private volatile boolean running = true;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter printWriter = new PrintWriter(outputStream)) {
while (running) {
String message = br.readLine();
if (message == null) {
break;
}
System.out.println("Come from client >" + message);
printWriter.write("echo " + message+"\n");
printWriter.flush();
}
} catch (IOException e) {
//自动关闭的时候 将running
this.running = false;
}finally {
this.stop();
}
}
public void stop() {
if (!running) {
return;
}
this.running = false;
try {
this.socket.close();
} catch (IOException e) {
}
}
}
public class AppServerClient {
public static void main(String[] args) throws InterruptedException, IOException {
AppServer server = new AppServer(12135);
server.start();
Thread.sleep(20_000L);
server.shutdown();
}
}
5、mac telnet模拟客户端输入
bogon:~ kpioneer$ telnet localhost 12135
Trying ::1...
Connected to localhost.
Escape character is '^]'.
hello
echo hello
I love you
echo I love you
Connection closed by foreign host.
服务端输出:
Come from client >hello
Come from client >I love you
dispose
总结:
可以看到,在子类使用两阶段终止模式时,其只需要实现各自所需要执行的任务,并且更新当前任务的数量即可。在某些情况下,当前任务的数量也可以不进行更新,比如在进行终止时,不关心当前剩余多少任务需要执行。
来源:https://juejin.cn/post/7023614457683116069
猜你喜欢
- 前言我们在学习Windows应用程序开发中,经常会用到消息对话框给用户或者管理员一些的消息提示,它们都是基于对MessageBox类的消息对
- 本文实例讲述了Java计算文本MD5加密值的方法。分享给大家供大家参考,具体如下:java计算文本MD5值,用于加密import java.
- 1. Spring简介Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。2. Spring的优势 1.方便解耦, 简
- 因为在Action的execute方法声明时就抛出了Exception异常,所以我们无需再execute方法中捕捉异常,仅需在struts.
- IOS与网页JS交互随着移动APP的快速迭代开发趋势,越来越多的APP中嵌入了html网页,但在一些大中型APP中,尤其是电商类
- 一、流程和任务的关系以下是一个简单的请假流程图,其中有一个开始事件,两个用户任务,一个结束事件。启动流程后,activiti会自动创建第一个
- Java常用类库Math类Math包含用于执行基本数字运算的方法,例如基本指数,对数,平方根和三角函数一、Field SummaryModi
- 1. 前言Spring最重要的一个概念当属Bean了,我们写的Controller、Service、Dao凡是加了对应注解交给Spring管
- 本文实例讲述了Android编程开发之TextView单击链接弹出Activity的方法。分享给大家供大家参考,具体如下:话不多说直接上码:
- servlet、filter、listener、interceptor之间的区别和联系一、概念1.servlet:servlet是一种运行服
- 一、代码先上代码(以下这段代码会有多种执行结果)@Testpublic void test_interrupted_thread() thr
- 一.解析概念StringUtils概念StringUtils 方法的操作对象是 Java.lang.String 类型的对象,是 JDK 提
- 简单理解泛型泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。通俗点将就是“类型的变量
- 本文实例讲述了Android编程基于自定义View实现绚丽的圆形进度条功能。分享给大家供大家参考,具体如下:本文包含两个组件,首先上效果图:
- 1、右值1.1 简介首先区分一下左右值:左值是指存储在内存中、有明确存储地址(可取地址)的数据;右值是指可以提供数据值的数据(不可取地址)如
- 一. SpringBoot中实现Session共享1. 创建web项目我们按照之前的经验,创建一个web程序,并将之改造成Spring Bo
- 本文实例为大家分享了Struts2框架拦截 器实例的示例代码,供大家参考,具体内容如下在看拦截 器的小例子的前我们先来看看sturts2的原
- 一些Java项目中在mybatis与spring整合中有MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接
- 之前一篇文章中我们讲了基于Mysql8的读写分离(文末有链接),这次来说说分库分表的实现过程。概念解析垂直分片按照业务拆分的方式称为垂直分片
- 异常分类可查的异常(checked exceptions):Exception下除了RuntimeException外的异常不可查的异常(u