`
沉沦的快乐
  • 浏览: 55733 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

并发编程之任务&线程的取消与关闭

阅读更多

任务或线程的取消与关闭

在向线程提交任务并且任务开始执行之后,通常任务执行完就自行停止和结束了,但是很多时候,我们希望在任务自行结束之前能提前终止,比如用户进行了取消操作等。具体来说,取消操作分为以下几种:

用户请求的取消操:比如用户按取消按钮,通过管理接口请求取消等;

超时停止:比如请求某个http请求,希望在指定时间还没返回结果时取消任务。

应用程序事件:比如寻找迷宫出口,如果有一个线程找到了出口,那么终止所有寻找出口的任务

运行时错误错误:比如任务在执行过程中出现某种错误,需要提供取消和停止任务线程的策略。

应用程序关闭:比如一个提供web服务的集群,某台机器性能表现非常差,需要关闭该机器以便进行排查。

     启动一个线程和任务比较简单,但是java没有提供一个安全,快速,可靠的机制来停止线程或任务。下面是几种典型的停止任务和线程的方法。

取消或关闭线程的方法或线程的方法

标志位取消。即提供一个Boolean类型的变量,通过设置该变量来停止任务

class CancelableTask extends Thread {
   praivate volatile boolean canceled =false;
   public void run() {
        while(!canceled ) {
              doSomething();
        }
   }
   public void cancel(){canceled  = true;}
}

    这里的中断标志位使用了volatile修饰。不用锁的目的是为了性能,而加volatile的目的是保证可见性,即每次读的时候总能读到最近更新的值,而不是被寄存器等缓存住的值。

中断

  方法1中,执行完doSomething()方法之后在循环到while中判断canceled的值。 这中方法可能能帮你解决大部分问题,但是有一种情况可能一份比较严重的问题:如果doSomething()被阻塞,那么永远无法执行下一次循环,当然也无法再判断canceled来取消任务了。比如在doSomething()方法中调用了BlockingQueue的put或take方法。BlockingQueue的take在BlockingQueue为空时一直阻塞直到queue中有数据为止。所以这种情况下通过标志位的终止策略失效了,同时也产生了线程活跃度问题。

  为解决这个问题,java提供了一种协作机制——中断策略来取消任务或线程。每一个线程都有一个中断状态(interrupted status),在中断的时候该中断状态为true。java线程提供了几个中断方法处理中断,interrupt()方法中断线程,调用该方法并不意味着目标线程必定停止了工作,它仅仅只是向目标线程传递了中断消息,是否停止工作还取决于目标线程获得中断消息之后的处理策略。isinterrupted()返回线程的中断状态。interrupted()方法清楚线程中断状态并且返回之前的中断状态,该方法也是唯一清除中断状态的方法。

  

class InterruptableTask extends Thread {
   
   public void run() {
        try {
              while(!thread.currentThread.isInterruped())
                   doSomeThing();
       } catch(InterruptException e) {
              exitThread();
       }
   }
   public void cancel() {intertupt();}
}

      中断请求只是向目标线程发生了消息,目标线程获得中断信号之后会做什么情取决了中断策略,因此定制线程的中断策略是必须的。尽可能迅速退出,如果需要的话进行清理,可能的话通知实体线程已退出。如果代码不是线程的拥有者(线程池的线程拥有者就是线程池),那么应该小心的保存中断状态。

通过Future取消

   

public void futureTask() throws ExecutionException {
      Future<?> task = taskExec.submit();
     try{
          task.get(timeout,unit);
     } catch(TimeoutExecption e){
       doSomeClear();
     }catch(ExecutionException e){
      throw ExecutionException ;
     } finally {
        task.cancel(true);
    }
}

 

 处理不可中断阻塞

   很多阻塞的库通过提前返回和抛出interruptExceptin异常来实现对中断的响应,但是并不是所有的阻塞方法和阻塞机制都响应中断。如果一个线程是同步socket I/O或等待内部锁而阻塞的,那么线程除了能移除中断状态之外,什么都做不了。对于那些不可中断活动所阻塞的线程,可以提供类型中断响应的方法来处理,但是必须先了解线程是由于什么原因阻塞的,才能对症下药。下面是处理不可中断阻塞的几种方法。

 1.java io中的同步io,最常见的阻塞IO是读取和写入socket,但是 inputstream和outputstream的read,write方法都是不响应中断的,但是通过关闭底层的socket,可以让read,write抛出一个socketException。

2. java nio中的同步io,中断一个等待iterruptibleChannel的线程,会导致抛出ClosedByInterruptException并关闭链路(也会导致其他线程在这条链路的阻塞并抛出ClosedByInterruptException)。关闭一个等待iterruptibleChannel导致多个阻塞在链路操作上的线程抛出AsynchronousCloseException。大多数标准的channel都实现了iterruptibleChannel。

3 Selector的异步I/O,如果一个线程阻塞于Selector.select方法,close方法会导致它通过抛出ClosedSelectorException提前返回。

4.获得锁。如果用的是内部锁,那么如果不能保证它最终能获得锁,你是没有办法终止它的。但是显式Lock类提供了LockInterruptible方法,在等待锁的同时也能够响应中断。

ExecutorService关闭

    ExectuorService提供了两个关闭方法:shutdown和shutdownNow。shutdown能在队列中的任务都处理完之后优雅的关闭,所以安全性高,缺点是响应慢。shutdownNow强行关闭ExectuorService,所以响应速度更快,缺点是不安全,可能破坏数据结构。

致命毒丸

      致命毒丸是一种关闭关闭生产者消费者模式的方式。在生产者队列中放入一个特殊对象,消费者获得特殊对象之后,开始关闭线程。这种方式要求消费者在放入致命毒丸之后,不应该在放入新的任务。致命毒丸必须在生产者消费者线程数据已知的情况下使用。在多生产者模式下,每个生产者放入致命毒丸,在消费者接受到第N(生产者的数目)个毒丸之后,开始执行关闭操作。在多消费者模式下,让生产者放入N(消费者的数目)毒丸,不过这种请况必须消费者是轮询处理任务的,否则不能保证每个消费者都得到毒丸。致命毒丸的一个缺点是在多消费者多生产者模式下不太好应用,有一定的局限性。

 

分享到:
评论

相关推荐

    Java并发编程实战

    第7章 取消与关闭 第8章 线程池的使用 第9章 图形用户界面应用程序 第三部分 活跃性、性能与测试 第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第四部分 高级主题 第13章 显式锁 第...

    Java并发编程实践 PDF 高清版

    本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5,6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。 目录 代码清单 序 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的...

    Java 并发编程实战

    第7章 取消与关闭 第8章 线程池的使用 第9章 图形用户界面应用程序 第三部分 活跃性、性能与测试 第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第四部分 高级主题 第13章 显式锁 第...

    Java并发编程实战2019.zip

    第5章 基础构建模块 第6章 任务执行 第7章 取消与关闭 第8章 线程池的使用 第9章 图形用户界面应用程序 第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第13章 显式锁 第14章 构建...

    Java并发编程实战-读书笔记

    《Java并发编程实战》个人读书笔记,非常详细: 1 简介 2 线程安全性 3 对象的共享 4 对象的组合 5 基础构建模块 6 任务执行 7 取消与关闭 8 线程池的使用 9 图形用户界面应用程序 10 避免活跃性危险 11 性能与可...

    JAVA并发编程实践_中文版(1-16章全)_1/4

    8.1 任务与执行策略问的隐性耦合 8.2 定制线程池的大小 8.3 配置threadpoolexecutor 8.4 扩展threadpoolexecutor 8.5 并行递归算法 第9章 gui应用程序 9.1 为什么gui是单线程化的 9.2 短期的gui任务 9.3 耗时gui任务...

    Java并发编程part2

    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...

    Java并发编程实践part1

    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...

    Java并发编程(学习笔记).xmind

    Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 ...

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    5.1 声明式编程与visual basic 256 5.2 使用xaml创建窗口 257 5.3 xaml语法 260 5.3.1 xaml语言基础 261 5.3.2 使用xaml声明工作流 264 5.4 小结 265 第6章 异常处理和调试 267 6.1 visual studio ...

    notes-learning-java-concurrency:java 并发学习笔记

    5.任务异步返回结果和取消关闭。以前的线程实现都是没有返回值的,但java5中,有返回值的线程是如何实现的呢? 6.显式锁。协调共享对象访问,在java5以前用synchronized实现,现在可以用Lock显式的lock()和unlock(),...

    C#编程经验技巧宝典

    C#编程经验技巧宝典源代码,目录如下: 第1章 开发环境 1 &lt;br&gt;1.1 Visual Studio开发环境安装与配置 2 &lt;br&gt;0001 安装Visual Studio 2005开发环境须知 2 &lt;br&gt;0002 配置合适的Visual Studio 2005...

    javaSE代码实例

    第16章 多线程——Java中的并发协作 343 16.1 线程的基本知识 343 16.1.1 多线程编程的意义 343 16.1.2 定义自己的线程 344 16.1.3 创建线程对象 345 16.1.4 启动线程 347 16.1.5 同时使用多个线程 ...

    c#学习笔记.txt

    volatile指示字段可由操作系统、硬件或并发执行的线程等在程序中进行修改。 9,语句 语句是程序指令。除非特别说明,语句都按顺序执行。C# 具有下列类别的语句。 类别C# 关键字 选择语句if, else, switch, case 迭代...

    asp.net知识库

    C++ 泛型编程系列讲座之实施 泛型技巧系列:简单类型选择器 C# 泛型简介 我眼中的C#2.0新功能特性 泛型技巧系列:避免基类及接口约束 New Article 不该用Generics实现Abstract Factory的理由 C#2.0-泛型 C#2.0-...

    IIS6.0 IIS,互联网信息服务

    请与系统管理员联系。 点击文档:可以设置网站默认首页,推荐删除iisstart.asp,添加index.asp和index.htm   点击目录安全性:点击编辑可以对服务器访问权限进行设置 8、把凡人网络购物系统V7.0文件复制到你选择的...

    易语言-Hp-Socket For E 5.4.2 高性能TCP/UDP通信组件 源码+模块+例程

    1、所有可能导致 Socket 关闭的组件接口方法都在 Socket 通信线程中异步触发 OnClose 事件 2、Server 与 Agent 组件的 DIRECT 发送策略也支持通过 GetPendingDataLength() 方法实现流控 3、Server 与 Agent 组件的 ...

Global site tag (gtag.js) - Google Analytics