停止基于线程的服务

应用程序通常会创建拥有多个线程的服务,如 线程池 即可以表示一种拥有多个线程的服务。应用程序退出时,这些服务所拥有的线程也应该结束,JVM 才能被正常关闭。

在 Java 中,线程由 Thread 对象表示,且和其它对象一样可以被自由共享。此外,线程有其所有者,即创建该线程的类,如线程池即是其工作者线程的所有者,应该通过线程的所有者来操控它们。

和其它封装对象一样,线程的所有权是不可传递的:应用程序可以拥有服务,服务也可以拥有工作者线程,但应用程序不能拥有工作者线程,因此应用程序不能直接停止工作者线程。相反,服务应该提供生命周期方法(Lifecycle Method)来关闭它自己以及它所拥有的线程。如在 ExecutorService 中提供了 shutdown 和 shutdownNow 等生命周期方法。

总之,对于持有线程的服务,只要服务的存在时间大于创建线程的方法的存在时间,那么就应该提供生命周期方法。

日志服务

LogWriter

LogWriter 提供了一种简单的日志服务示例。该示例中,产生日志的线程不会直接将消息写入输出流,而是由 LogWriter 通过 BlockingQueue 将消息提交到日志线程,并由日志线程写入到输出流。这是一种多生产者单消费者的设计模式:每个调用 log 的操作都相当于生产者,而后台的日志线程相当于消费者。如果消费者的处理速度低于生产者的生产速度,那么 BlockingQueue 将阻塞生产者。LogWriter 相当于一种日志服务,而真正负责写入日志消息的则是该服务所拥有的工作线程。LogWriter 并没有提供关闭服务的方法可以使得结束服务的同时关闭其所持有的工作线程。

public class LogWriter {private final BlockingQueue<String> queue;private final LoggerThread logger;private final int CAPACITY = 100;public LogWriter(PrintWriter writer) {this.queue = new LinkedBlockingDeque<String>(CAPACITY);this.logger = new LoggerThread(writer);}public void start() {logger.start();}public void log(String msg) throws InterruptedException {queue.put(msg);}private class LoggerThread extends Thread {private final PrintWriter writer;public LoggerThread(PrintWriter writer) {this.writer = writer;}public void run() {try {while (true) {writer.println(queue.take());}} catch (InterruptedException ignored) {} finally {writer.close();}}}
}

ClosableLogWriter

为 LogWriter 添加关闭服务的生命周期方法有多种方式,下面详细讨论这些方式的利弊:

第一种方式是利用线程中断:由于日志线程会反复调用 take,而 take 可以响应中断。如果日志线程在捕捉到 InterruptedException 时的处理机制是退出,那么只需中断日志线程即可。然而这种方式并不完备,因为直接关闭的做法会丢失正在等待被写入到日志的信息,此外,其它线程在调用 log 时会被阻塞,且阻塞状态无法解除。

还有一种方式是设置某个“请求关闭”标志:通过判断该标志可以避免其它线程继续提交日志消息,当收到关闭请求后,消费者将队列中的所有消息写入日志,并解除所有在调用 log 时阻塞的生产者。但这种实现是一种 “先判断再运行” 的代码序列存在竞争条件的问题,仍然可能导致线程阻塞在 log 方法且无法解除的现象(可以通过一些技巧降低这类情况发生的概率,如在宣布队列被清空之前,让消费者等待数秒钟,但依然无法保证可靠性)。

为了解决竞态条件问题,要让日志的提交操作成为原子操作,但我们不希望在消息加入队列时去持有一个锁,因为 put 方法本身就可以阻塞。故采用的方法是:通过原子方式来检查关闭请求,并且有条件地递增一个计数器来“保持”提交消息的权力。ClosableLogWriter 实现了这一策略。

public class ClosableLogWriter {private final BlockingQueue<String> queue;private final PrintWriter writer;private final LoggerThread loggerThread;private final int CAPACITY = 100;@GuardedBy("this") private boolean isShutdown;@GuardedBy("this") private int reservations;public ClosableLogWriter(PrintWriter writer) {this.queue = new LinkedBlockingDeque<>(CAPACITY);this.writer = writer;this.loggerThread = new LoggerThread();}public void start() {loggerThread.start();}public void stop() {synchronized (this) { isShutdown = true; }loggerThread.interrupt();}public void log(String msg) throws InterruptedException {synchronized (this) {if (isShutdown)throw new IllegalStateException();++reservations;}queue.put(msg);}private class LoggerThread extends Thread {public void run() {try {while (true) {try {synchronized (ClosableLogWriter.this) {if (isShutdown && reservations == 0)break;}String msg = queue.take();synchronized (ClosableLogWriter.this) {--reservations;}writer.println(msg);} catch (InterruptedException e) {/* retry */}}} finally {writer.close();}}}
}

LogService

LogService 将管理工作线程的工作委托给了 ExecutorService,而不是由其自行管理。

public class LogService {private final PrintWriter writer;private final ExecutorService exec = Executors.newSingleThreadExecutor();private final int TIMEOUT = 10;private final TimeUnit UNIT = TimeUnit.SECONDS;public void start() {}public LogService(PrintWriter writer) {this.writer = writer;}public void stop() throws InterruptedException {try {exec.shutdown();exec.awaitTermination(TIMEOUT, UNIT);} finally {}}public void log(String msg) {try {exec.execute(new WriteTask(msg));} catch(RejectedExecutionException ignored) {}}private class WriteTask implements Runnable {private final String msg;public WriteTask(String msg) {this.msg = msg;}public void run() {writer.println(msg);}}
}

停止基于线程的服务(一)相关推荐

  1. 基于Springcloud的服务治理落地实践

    前言 在微服务盛行的今天,提起服务治理,相信大家都已经不再陌生,许多公司都有自己内部的一套定制化的实现方案, Access也不例外, 接下来, 我来为大家介绍一下我们的一套基于Springcloud的 ...

  2. java线程怎么重启_如何在Java中启动/停止/重启线程?

    10 个答案: 答案 0 :(得分:41) 一旦线程停止,您就无法重新启动它.但是,没有什么可以阻止您创建和启动新线程. 选项1:创建一个新线程,而不是尝试重新启动. 选项2:而不是让线程停止,让它等 ...

  3. 高性能dhcp服务器,基于线程池机制的高性能DHCP服务器研究与实现

    摘要: 随着互联网的蓬勃发展,IP地址资源越来越紧张.DHCP服务是在现有IPv4协议基础上解决IP地址资源短缺问题的有效途径. 目前,多数DHCP服务器是单线程运行,串行处理客户请求的.其应用于大型 ...

  4. dhcp计算机毕业论文,基于线程池机制的高性能DHCP服务器研究与实现-计算机科学与技术专业毕业论文.docx...

    文档介绍: 西北丁业大学硕士学位论文 摘要摘 要随着互联网的蓬勃发展,IP地址资源越来越紧张.DHCP服务是在现有IPv4协议基础上解决IP地址资源短缺问题的有效途径.目前,多数DHCP服务器是单线程 ...

  5. auto.js停止所有线程_十年架构师带你快速上手多线程

    这世上有三样东西是别人抢不走的:一是吃进胃里的食物,二是藏在心中的梦想,三是读进大脑的书 多线程快速入门 1.线程与进程区别 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程.线程是一组 ...

  6. Web Api 基于Zookeeper的服务注册与发现

    差异 基于Nginx的服务提供和消费 基于zookeeper的服务注册和发现 zk的负载均衡是可以调控,nginx只是能调权重,其他需要可控的都需要自己写插件:但是nginx的吞吐量比zk大很多,可以 ...

  7. auto.js停止所有线程_Java线程与并发编程实践:深入理解volatile和final变量

    同步有两种属性:互斥性和可见性.synchronized关键字与两者都有关系.Java同时也提供了一种更弱的.仅仅包含可见性的同步形式,并且只以volatile关键字关联. 假设你自己设计了一个停止线 ...

  8. 基于docker微服务架构_使用基于微服务的流架构更好地进行大规模的复杂事件处理(第1部分)...

    基于docker微服务架构 基于微服务的流架构与开源规则引擎相结合,使实时业务规则变得容易 这篇文章旨在详细介绍我将OSS业务规则引擎与Kafka风格的现代流消息传递系统集成在一起的项目. 该项目的目 ...

  9. NGINX配置基于Node.js服务的负载均衡服务器

    NGINX配置基于Node.js服务的负载均衡服务器 本部署指南说明了如何使用NGINX开源和NGINX Plus在Node.js应用程序服务器池之间平衡HTTP和HTTPS通信.本指南中的详细说明适 ...

最新文章

  1. [NOIP2016] 组合数问题
  2. android拨打电话
  3. 每天一个 liunx 命令 ls
  4. MVC3 Razor @RenderSection
  5. Windows下安装Python模块时环境配置
  6. 从零开始学视觉Transformer(2):图像与Transformer基础
  7. 日语学习-多邻国-兴趣爱好
  8. 命令进入mysql创建jira_JIRA使用教程:连接数据库—MySQL_MySQL
  9. std::set, std::list, std::vector在erase的区别
  10. vue搭建cli脚手架环境(出现问题及解决,主要是node版本低)
  11. oracle中冗余,各位有没有检查冗余索引的脚本
  12. DotNetTextBox V3.0 所见即所得编辑器控件Ver3.2.5 Free(免费版)
  13. 利用反射实现工厂模式
  14. php facebook授权登录获取头像_IdentityServer4从数据库获取User登录并对Claims授权验证(五)...
  15. 第22节 软件工程知识
  16. 基于stm32单片机外文文献_13个基于STM32的经典项目设计实例,全套资料~-嵌入式系统-与非网...
  17. 基于氚云平台的应用开发学习(一)
  18. 使用Matlab理解PID
  19. 【心电信号】基于matlab心率检测【含Matlab源码 1993期】
  20. 沟通的艺术——情绪:感觉、思考和沟通

热门文章

  1. 潭州课堂25班:Ph201805201 django 项目 第四十三课 后台 用户管理前后功能实现 (课堂笔记)...
  2. 高斯消元_fortran
  3. Qt/C++编写安防视频监控系统20-录像机管理
  4. 精简版openwrt配置frpc
  5. 假蜜蜂怎么鉴别?如何鉴定蜂蜜的好坏?
  6. 如何用Eclipse调试(debug)Java代码?
  7. java毕业设计旅游网站mybatis+源码+调试部署+系统+数据库+lw
  8. 解决ECharts 因X轴数据过多导致重叠显示不全的问题
  9. 世界的规则――给小牛同学的汇编语言推荐书
  10. Java语言连接MongoDB常用的方法