`
沉沦的快乐
  • 浏览: 56989 次
  • 性别: 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多线程并发编程

    首先,Java并发编程的核心在于管理线程的并发执行。线程并发的使用可以显著提升程序处理能力,例如在服务器端处理大量用户请求时,如果每个请求都由单独的线程处理,那么处理速度将大大提高。但同时,多线程并发也会...

    JAVA并发编程实践-线程的关闭与取消-学习笔记

    在Java并发编程中,线程的关闭和取消是一项重要的任务,因为不正确的处理可能导致数据不一致、资源泄漏等问题。在Java中,强制停止线程并不是一个推荐的做法,因为这可能会导致系统状态的不稳定。传统的`Thread.stop...

    Java并发编程实战华章专业开发者书库 (Tim Peierls 等 美Brian Goetz).pdf

    《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南,由Tim Peierls等人与Brian Goetz合著,旨在帮助Java开发者理解和掌握在多线程环境中编写高效、安全的代码。这本书由拥有丰富经验的JDK并发大师及...

    Java 并发编程实战

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

    Java并发编程实践 PDF 高清版

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

    go并发编程实践V2+源代码

    并发编程是现代软件开发中的一个重要概念,它允许程序同时执行多个任务,从而提高系统的效率和响应性。Go语言通过goroutines和channels这两大数据结构实现了轻量级线程和进程间的通信,使得并发编程变得简单且高效。...

    Java并发编程示例(三):线程中断

    在实际的并发编程中,线程中断常用于取消任务、响应外部事件或者优雅地关闭资源。它与`Thread.stop()`方法不同,后者是不推荐使用的,因为它可能导致数据损坏和死锁。线程中断是一种更加安全和推荐的方式来控制线程...

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

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

    C语言多线程编程之线程池

    在IT领域,尤其是在系统编程和并发处理中,C语言多线程编程是一个重要的主题。线程池是一种优化的线程管理技术,它提高了系统资源的利用率,并降低了线程的创建和销毁开销。本文将深入探讨C语言中的线程池及其相关...

    java并发(二十四)多线程结果组装

    在Java编程中,多线程是并发处理任务的关键技术,特别是在高性能、高并发的应用场景下。本篇将探讨“多线程结果组装”的主题,它涉及到如何在多个并发执行的任务完成后,有效地收集并整合这些任务的结果。这个过程...

    Java并发编程实战1

    其中,任务执行、取消与关闭、线程池的使用等章节,讲解了如何有效地组织和管理线程,以避免并发问题。线程池允许我们预先创建一组线程,按需分配任务,从而提高系统效率并简化管理。 第三部分关注活跃性、性能和...

    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多线程编程实例

    在Java编程领域,多线程是一项至关重要的技术,它使得程序可以同时执行多...虽然年代久远,但这些原理和技巧在今天仍然适用,对于想要深入理解和掌握Java并发编程的开发者来说,这本书的源码实例无疑是宝贵的参考资料。

    Java并发编程实践part1

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

    java并发编程-构建块

    1. **线程**:Java中的线程是并发编程的基础,每个线程代表了程序的单一执行流。通过创建和管理线程,开发者可以实现多任务并行执行,提高系统性能。`Thread`类提供了创建线程的方法,如`start()`用于启动线程,`run...

    JUC并发编程学习笔记(硅谷)

    Java并发编程是Java开发中的重要领域,特别是在大型分布式系统或者高并发应用中,对线程安全和性能优化的理解与实践至关重要。"JUC并发编程学习笔记(硅谷)"很可能包含了关于Java并发工具集(Java Util Concurrency, ...

    BlockingQueue队列自定义超时时间取消线程池任务

    在Java编程中,`BlockingQueue`是一个非常重要的并发工具类,它主要用于线程间的数据通信。`newFixedThreadPool`是`java.util.concurrent`包中的一个线程池工厂方法,用于创建固定数量线程的线程池。`FutureTask`则...

Global site tag (gtag.js) - Google Analytics