任务或线程的取消与关闭
在向线程提交任务并且任务开始执行之后,通常任务执行完就自行停止和结束了,但是很多时候,我们希望在任务自行结束之前能提前终止,比如用户进行了取消操作等。具体来说,取消操作分为以下几种:
用户请求的取消操:比如用户按取消按钮,通过管理接口请求取消等;
超时停止:比如请求某个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中,强制停止线程并不是一个推荐的做法,因为这可能会导致系统状态的不稳定。传统的`Thread.stop...
《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南,由Tim Peierls等人与Brian Goetz合著,旨在帮助Java开发者理解和掌握在多线程环境中编写高效、安全的代码。这本书由拥有丰富经验的JDK并发大师及...
第7章 取消与关闭 第8章 线程池的使用 第9章 图形用户界面应用程序 第三部分 活跃性、性能与测试 第10章 避免活跃性危险 第11章 性能与可伸缩性 第12章 并发程序的测试 第四部分 高级主题 第13章 显式锁 第...
本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5,6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。 目录 代码清单 序 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的...
并发编程是现代软件开发中的一个重要概念,它允许程序同时执行多个任务,从而提高系统的效率和响应性。Go语言通过goroutines和channels这两大数据结构实现了轻量级线程和进程间的通信,使得并发编程变得简单且高效。...
在实际的并发编程中,线程中断常用于取消任务、响应外部事件或者优雅地关闭资源。它与`Thread.stop()`方法不同,后者是不推荐使用的,因为它可能导致数据损坏和死锁。线程中断是一种更加安全和推荐的方式来控制线程...
Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 ...
在IT领域,尤其是在系统编程和并发处理中,C语言多线程编程是一个重要的主题。线程池是一种优化的线程管理技术,它提高了系统资源的利用率,并降低了线程的创建和销毁开销。本文将深入探讨C语言中的线程池及其相关...
在Java编程中,多线程是并发处理任务的关键技术,特别是在高性能、高并发的应用场景下。本篇将探讨“多线程结果组装”的主题,它涉及到如何在多个并发执行的任务完成后,有效地收集并整合这些任务的结果。这个过程...
其中,任务执行、取消与关闭、线程池的使用等章节,讲解了如何有效地组织和管理线程,以避免并发问题。线程池允许我们预先创建一组线程,按需分配任务,从而提高系统效率并简化管理。 第三部分关注活跃性、性能和...
8.1 任务与执行策略问的隐性耦合 8.2 定制线程池的大小 8.3 配置threadpoolexecutor 8.4 扩展threadpoolexecutor 8.5 并行递归算法 第9章 gui应用程序 9.1 为什么gui是单线程化的 9.2 短期的gui任务 9.3 耗时gui任务...
中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...
在Java编程领域,多线程是一项至关重要的技术,它使得程序可以同时执行多...虽然年代久远,但这些原理和技巧在今天仍然适用,对于想要深入理解和掌握Java并发编程的开发者来说,这本书的源码实例无疑是宝贵的参考资料。
中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...
1. **线程**:Java中的线程是并发编程的基础,每个线程代表了程序的单一执行流。通过创建和管理线程,开发者可以实现多任务并行执行,提高系统性能。`Thread`类提供了创建线程的方法,如`start()`用于启动线程,`run...
Java并发编程是Java开发中的重要领域,特别是在大型分布式系统或者高并发应用中,对线程安全和性能优化的理解与实践至关重要。"JUC并发编程学习笔记(硅谷)"很可能包含了关于Java并发工具集(Java Util Concurrency, ...
在Java编程中,`BlockingQueue`是一个非常重要的并发工具类,它主要用于线程间的数据通信。`newFixedThreadPool`是`java.util.concurrent`包中的一个线程池工厂方法,用于创建固定数量线程的线程池。`FutureTask`则...