[本文是我对Java Concurrency In Practice 7.2的归纳和总结. 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正. ]
以ExecutorService为例, 该类内部封装有多个线程, 类外部无法直接停止这些线程. 相反, 外部调用Service的shutDown和shutDownNow方法关闭Service, 而Service负责停止其拥有的线程.
大多数server应用会使用到log, 下例中的LogWriter是一个使用生产者消费者模式构建的log service, 需要打印log的线程将待打印的内容加入到阻塞队列中, 而logger线程则不断的从阻塞队列中取出数据输出:
public class LogWriter {
private final BlockingQueue<String> queue;
private final LoggerThread logger;
public LogWriter(Writer writer) {
this.queue = new LinkedBlockingQueue<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 void run() {
try {
while (true)
writer.println(queue.take());
} catch (InterruptedException ignored) {
} finally {
writer.close();
}
}
}
}
LogWriter内部封装有LoggerThread线程, 所以LogWriter是一个基于线程构建的Service. 根据ExecutorService的经验, 我们需要在LogWriter中提供停止LoggerThread线程的方法. 看起来这并不是很难, 我们只需在LogWriter中添加shutDown方法:
/**
* 该方法用于停止LoggerThread线程
*/
public void shutDown() {
logger.interrupt();
}
当LogWriter.shutDown方法被调用时, LoggerThread线程的中断标记被设置为true, 之后LoggerThread线程执行queue.take()方法时会抛出InterruptedException异常, 从而使得LoggerThread线程结束.
然而这样的shutDown方法并不是很恰当:
1. 丢弃了队列中尚未来得及输出的数据.
2. 更严重的是, 假如线程A对LogWriter.log方法的调用因为队列已满而阻塞, 此时停止LoggerThread线程将导致线程A永远阻塞在queue.put方法上.
对上例的改进:
public class LogWriter {
private final BlockingQueue<String> queue;
private final LoggerThread loggerThread;
private final PrintWriter writer;
/**
* 表示是否关闭Service
*/
private boolean isShutdown;
/**
* 队列中待处理数据的数量
*/
private int reservations;
public void start() {
loggerThread.start();
}
public void shutDown() {
synchronized (this) {
isShutdown = true;
}
loggerThread.interrupt();
}
public void log(String msg) throws InterruptedException {
synchronized (this) {
// service已关闭后调用log方法直接抛出异常
if (isShutdown)
throw new IllegalStateException("Service has been shut down");
++reservations;
}
// BlockingQueue本身就是线程安全的, put方法的调用不在同步代码块中
// 我们只需要保证isShutdown和reservations是线程安全的即可
queue.put(msg);
}
private class LoggerThread extends Thread {
public void run() {
try {
while (true) {
try {
synchronized (this) {
// 当service已关闭且处理完队列中的所有数据时才跳出while循环
if (isShutdown && reservations == 0)
break;
}
String msg = queue.take();
synchronized (this) {
--reservations;
}
writer.println(msg);
} catch (InterruptedException e) {
// 发生InterruptedException异常时不应该立刻跳出while循环
// 而应该继续输出log, 直到处理完队列中的所有数据
}
}
} finally {
writer.close();
}
}
}
}
上面的处理显得过于复杂, 利用ExecutorService可以编写出相对更简洁的程序:
public class LogService {
/**
* 创建只包含单个线程的线程池, 提交给该线程池的任务将以串行的方式逐个运行
* 本例中, 此线程用于执行打印log的任务
*/
private final ExecutorService exec = Executors.newSingleThreadExecutor();
private final PrintWriter writer;
public void start() {
}
public void shutdown() throws InterruptedException {
try {
// 关闭ExecutorService后再调用其awaitTermination将导致当前线程阻塞, 直到所有已提交的任务执行完毕, 或者发生超时
exec.shutdown();
exec.awaitTermination(TIMEOUT, UNIT);
} finally {
writer.close();
}
}
public void log(String msg) {
try {
// 线程池关闭后再调用其execute方法将抛出RejectedExecutionException异常
exec.execute(new WriteTask(msg));
} catch (RejectedExecutionException ignored) {
}
}
private final class WriteTask implements Runnable {
private String msg;
public WriteTask(String msg) {
this.msg = msg;
}
@Override
public void run() {
writer.println(msg);
}
}
}
分享到:
相关推荐
本文将围绕“php_redis-5.2.1-7.2-ts-vc15-x86.zip”这个压缩包文件,详细讲解如何在Windows环境下安装和使用PHP Redis扩展,以及其关键组件和功能。 首先,从文件名可以看出,这个压缩包是专为PHP 5.2.1到7.2版本...
这个压缩包"php_imagick-3.4.4rc2-7.2-nts-vc15-x64.zip"是为PHP 7.2版本构建的,非线程安全(NTS)版本,采用Visual C++ 15编译器(VC15),并适用于64位Windows系统。本文将深入探讨PHP Imagick的使用和其中包含的...
标题 "php_redis-5.3.1-7.2-ts-vc15-x86.zip" 提供的信息表明,这是一个针对PHP的Redis扩展包,具体版本为5.3.1,适配PHP 7.2的线程安全(TS)版本,且是为32位(x86)Windows系统设计的。此扩展由Visual C++ 15...
1. **下载匹配的版本**:标题中提到的"php_imagick-3.4.4-7.2-nts-vc15-x64.zip"是专门为PHP 7.2的64位非线程安全(NTS)版本设计的。确保你已正确选择与你的PHP环境匹配的版本。 2. **解压并复制文件**:解压缩...
Express Logic's ThreadX for Win32 Demo Using Visual C/C++ This demo program is intended for use with the book titled "Real-Time Embedded Multithreading: Using ThreadX and ARM" by Edward L....
标题 "php-7.2.34-nts-Win32-VC15-x64.zip" 提供的信息是关于一个适用于Windows操作系统的PHP环境包,具体为版本7.2.34,非线程安全(NTS)版本,且是64位编译,由Visual C++ 15(VS2017)编译器构建。 PHP...
同时,Bandzip的多线程技术使得它在处理大文件或者批量文件时,能充分利用多核处理器的优势,极大地提高了工作效率。 其次,Bandzip的界面设计简洁,用户友好。它没有广告插件,提供了一个清爽的工作环境,让用户...
标题 "php_redis-php7.2-nts-vc15-x86" 指的是一个针对PHP 7.2版本的非线程安全(NTS)版本的Redis扩展,使用Visual C++ 15编译器(对应于Visual Studio 2017),适用于32位(x86)系统。这个扩展允许PHP应用程序与...
Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与...
JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多...
"qtcreator-gdb-7.2-mingw-x86"这个压缩包文件,如其名所示,是专门为在Windows x86平台上使用MinGW编译器的QT Creator设计的GDB 7.2版本。GDB 7.2是一个较旧但仍然稳定和功能丰富的调试版本,它与QT Creator结合...
基于Java多线程与线程安全实践-基于Http协议的断点续传设计与实现.zip基于Java多线程与线程安全实践-基于Http协议的断点续传设计与实现.zip基于Java多线程与线程安全实践-基于Http协议的断点续传设计与实现.zip基于...
标题中的"php-7.2.29-nts-Win32-VC15-x64.zip"标识了这是一个PHP的特定版本,具体是7.2.29,适用于Windows 64位操作系统。"Win32"在这里可能是个误写,因为"VC15"通常代表Visual C++ 2017编译器,它主要支持x64架构...
Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与...
Java多线程与线程安全实践-基于Http协议的断点续传.rarJava多线程与线程安全实践-基于Http协议的断点续传.rarJava多线程与线程安全实践-基于Http协议的断点续传.rarJava多线程与线程安全实践-基于Http协议的断点续传...
`apollo-adminservice-1.8.0-github.zip`是一个包含Apollo AdminService的1.8.0版本源代码和相关资源的压缩包,可以从GitHub获取。 首先,我们来看看`apollo-adminservice.conf`,这是Apollo AdminService的配置...
【标题】"php_redis-4.0.0-7.2-nts-vc15-x86.zip" 是一个PHP扩展包,专为PHP 7.2版本设计,用于支持Redis数据库的交互。"nts"代表"Non Thread Safe",意味着这个版本的扩展不适用于多线程环境,而适合于单线程或者...
标题 "php_redis-3.1.4-7.2-nts-vc15-x86最新版,绝对可用" 指的是一个针对PHP的Redis扩展包,版本为3.1.4,适用于PHP 7.2环境,是非线程安全(nts)版本,采用Visual C++ 15编译器构建,适用于x86架构的Windows系统...
【标题】"php-7.2.30-nts-Win32-VC15-x64.zip" 是一个PHP的Windows平台版本的压缩包,主要用于在本地环境中升级PHP到7.2.30版。这个版本是为64位操作系统设计的,由Visual C++ 15编译器(VC15)构建,并且是非线程...