并发编程—— 任务取消 之 停止基于线程的服务

Java并发编程实践 目录

并发编程—— ConcurrentHashMap

并发编程—— 阻塞队列和生产者-消费者模式

并发编程—— 闭锁CountDownLatch 与 栅栏CyclicBarrier

并发编程—— Callable和Future

并发编程—— CompletionService : Executor 和 BlockingQueue

并发编程—— 任务取消

并发编程—— 任务取消 之 中断

并发编程—— 任务取消 之 停止基于线程的服务

概述

第1 部分 问题描述

第2 部分 日志服务实例

参考

第1 部分 问题描述

  应用程序通常会创建拥有多个线程的服务,例如线程池,并且这些服务的生命周期通常比创建它们的方法的生命周期更长。如果应用程序准备退出,那么这些服务所拥有的线程也需要结束。由于无法通过抢占式的方法来停止线程,因此它们需要自行结束。

正确的封装原则是:除非拥有某个线程,否则不能对该线程进行操控。例如,中断线程或者修改线程的优先级等。

  与其他的封装对象一样,线程的所有权是不可传递的:应用程序可以拥有服务,服务也可以拥有工作者线程,但应用程序并不能拥有工作者线程,因此应用程序不能停止工作者线程。相反,服务应该提供生命周期方法,服务就可以关闭所有的线程了。这样,当应用程序关闭服务时,服务就可以关闭所有的线程了。

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

第2 部分 日志服务实例

  下面程序给出一个日志服务示例,其中日志操作在单独的日志线程中执行。产生日志消息的线程并不会将消息直接写入输出流,而是 LogWriter 通过 BlockingQueue 将消息提交给日志线程,并由日志线程写入。这是一种多生产者单消费者的设计方式:每个调用 log 的操作都相当于一个生产者,而后台的日志线程则相当于消费者。

 1 /**
 2  * 不支持关闭的生产者-消费者日志服务
 3  */
 4 public class LogWriter {
 5     private final BlockingQueue<String> queue;
 6     private final LoggerThread logger;
 7
 8     public LogWriter(Writer writer){
 9         this.queue = new LinkedBlockingDeque<String>();
10         this.logger = new LoggerThread(writer);
11     }
12
13     public void start(){
14         logger.start();
15     }
16
17     public void log(String msg) throws InterruptedException{
18         queue.put(msg);
19     }
20
21     private class LoggerThread extends Thread{
22         private final Writer writer;
23
24         public LoggerThread(Writer writer) {
25             this.writer = writer;
26         }
27
28         @Override
29         public void run() {
30             try {
31                 while(true){
32                     writer.write(queue.take());
33                 }
34             } catch (IOException e) {
35                 // io exception handle
36             } catch (InterruptedException e) {
37                 // interrupt exceptino handle
38             } finally{
39                 try {
40                     writer.close();
41                 } catch (IOException e) {
42                     e.printStackTrace();
43                 }
44             }
45         }
46     }
47 }

下面给出 向LogWriter 添加可靠的取消操作的程序:

 1 /**
 2  * 7.15 向LogWriter 添加可靠的取消操作
 3  *
 4  * @ClassName: LogService
 5  * @author xingle
 6  * @date 2014-10-24 下午4:35:57
 7  */
 8 public class LogService {
 9
10     private final BlockingQueue<String> queue;
11     private final LoggerThread loggerThread;
12     private final PrintWriter writer;
13     @GuardedBy("this")
14     private boolean isShutdown;
15     @GuardedBy("this")
16     private int reservations;
17
18     public LogService(Writer writer) {
19         this.queue = new LinkedBlockingDeque<String>();
20         this.loggerThread = new LoggerThread();
21         this.writer = new PrintWriter(writer);
22     }
23
24     public void start() {
25         loggerThread.start();
26     }
27
28     public void stop() {
29         synchronized (this) {
30             isShutdown = true;
31         }
32         loggerThread.interrupt();
33     }
34
35     /**
36      * 为LogService 提供可靠关闭操作的方法是解决竞态条件问题,因而要使日志消息的提交操作作为原子操作,
37      * 然而 ,不希望在消息加入队列时去持有一个锁,因为 put 方法本身就可以阻塞
38      * 这里 采用的方法是:通过原子方式来检查关闭请求,并且有条件地递增一个计数器来“保持”提交消息的权利
39      */
40     public void log(String msg) throws InterruptedException {
41         synchronized (this) {
42             if (isShutdown)
43                 throw new IllegalStateException();
44             ++reservations;
45         }
46         queue.put(msg);
47     }
48
49     /**
50      * 消费日志线程
51      */
52     private class LoggerThread extends Thread {
53         public void run() {
54             try {
55                 while (true) {
56                     try {
57                         synchronized (LogService.this) {
58                             if (isShutdown && reservations == 0)
59                                 break;
60                         }
61                         String msg = queue.take();
62                         synchronized (LogService.this) {
63                             --reservations;
64                         }
65                         writer.println(msg);
66                     } catch (InterruptedException e) {
67                         /* retry */
68                     }
69
70                 }
71             } finally {
72                 writer.close();
73             }
74         }
75     }
76 }


参考

1.《并发编程》 7.2 停止基于线程的服务

时间: 2024-08-10 21:28:03

并发编程—— 任务取消 之 停止基于线程的服务的相关文章

java并发编程6.取消与关闭

如果外部代码能在某个操作正常完成之前将其置入"完成"状态,那么这个操作就可以称为可取消的. Java没有提供任何机制来安全地终止线程.但它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的当前工作. 其中一种协作机制能设置某个"已请求取消"的标志,而任务将定期地查看该标志,如果设置了这个标志,那么任务将提前结束. 自定义取消机制 /** * 素数生成器 */ private class PrimeGenerator implements Runnable{

Java并发编程学习笔记(一)线程安全性 1

什么是线程安全性: 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化. 一个对象是否需要线程安全的,取决于他是否被多个线程访问.这指的是在程序中访问对象的方式,而不是对象要实现的功能.要使得对象时线程安全的,需要采用同步机制来协同对对象可变状态的访问.如果无法实现协同,那么可能导致数据破坏以及其他不该出现的结果. 如果当多个线程访

[笔记][Java7并发编程实战手册]第三章-线程同步辅助类-概要

[笔记][Java7并发编程实战手册]系列目录 有点着急了,没有太注重质量,自己也没有理解透,从本章起,读书和随笔笔记的质量会更好. 第三章 在本章中,我们将学习: 资源的并发访问控制 资源的多副本的并发访问控制 等待多个并发事件的完成 在集合点的同步 并发阶段任务的运行 并发阶段任务中的阶段交换 并发任务间的数据交换 回顾 在第二章中主要学习了以下接口 synchronized关键字 Lock接口以及实现类,如ReentrantLock.ReentrantReadWriteLock中的Read

那些年读过的书《Java并发编程实战》一、构建线程安全类和并发应用程序的基础

1.线程安全的本质和线程安全的定义 (1)线程安全的本质 并发环境中,当多个线程同时操作对象状态时,如果没有统一的状态访问同步或者协同机制,不同的线程调度方式和不同的线程执行次序就会产生不同的不正确的结果.要确保获得最后正确的结果就需要对线程访问对象状态 的操作上进行同步或者协同,使多个线程无论在什么样的调度方式和线程执行顺序的情况中,都能产生正确的结果. 线程安全的本质就对(对象)状态的访问操作进行统一管理,使之在不同的执行环境下均能产生正确的结果.也就是在不同的并发环境下,保持对象状态的不变

并发编程—— 任务取消

Java并发编程实践 目录 并发编程—— ConcurrentHashMap 并发编程—— 阻塞队列和生产者-消费者模式 并发编程—— 闭锁CountDownLatch 与 栅栏CyclicBarrier 并发编程—— Callable和Future 并发编程—— CompletionService : Executor 和 BlockingQueue

这个例子展示了如何用C#编程实现启动、停止和重启Windows服务。

启动服务 下面的方法尝试通过指定的服务名称启动服务.然后等待知道服务运行或发生超时. [C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void StartService(string serviceName, int timeoutMilliseconds) {   ServiceController service = new ServiceController(serviceName);   try   {     TimeSpa

Java并发编程(十二):线程池的使用(转载)

本文转载自:http://www.cnblogs.com/dolphin0520/p/3932921.html 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.

并发编程:取消与关闭

1.取消标志:任务可能永远不会检查取消标志,如BlockingQueue.put阻塞操作 2.中断: 1)它并不是真正地中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己 2)中断是实现取消的最合理方式 3)除非你知道中断该线程的含义,否则就不应该中断这个线程 4)中断策略:尽快退出执行流程 4.响应中断 1)传递异常 2)恢复中断状态(再次调用interrupt) 注:只有实现了线程中断策略的代码才可以屏蔽中断请求 5.在专门的线程中中断任务:Rethrowabl

Java 并发编程(一)浅谈线程安全

首先我们要弄清楚什么叫线程安全. "线程安全"是指:当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 这里有三个关键点, 第一."线程安全"问题是来源于多个线程访问的情况下,当个线程没有竞争便涉及到出现线程安全的问题. 第二.类的"线程安全"性不依赖于多线程的执行顺序. 第三.主调代码不需要同步或协同.某个类"