java并发编程实战读书笔记--第七章

7:取消和关闭

java没有提供任何机制来安全的终止线程(stophe suspend不安全,不要使用)
但提供了中断机制,使得一个线程终止另一个线程。

7.1:任务取消

1、任务取消的原因:用户请求取消、有时间限制的操作、应用程序事件、错误、关闭等。java中没有一种安全的抢占式方法来停止线程。
2、可取消的任务必须有取消策略

  • How:其他代码如何(How)请求取消任务
  • 任务在何时(When)检查是否已经请求了取消
  • 在响应取消请求时应该执行哪些(What)操作

7.1.1:中断

1、在文中提到使用volatile类型的状态变量来取消任务,但不可靠。因为所执行的任务如果阻塞,就不会去检查状态变量,因此线程永远不会退出。
2、应该使用中断来取消任务的执行,中断并不会真正中断一个正在运行的线程,而是发出中断请求,然后由线程在下一个合适的时刻中断自己。

  • interrupt:中断目标线程
  • isInterrupted:返回目标线程的中断状态
  • interrupted:静态方法,清除当前线程的中断状态,并返回它之前的值
    3、线程的六种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED下的中断反应
  • NEW:new状态表示还未调用start方法,线程还未启动。在这个状态下调用中断方法不会设置线程的中断标志位。(线程还没启动中断个鬼)
  • RUNNABLE:该状态下线程分为两种情形:没有得到CPU调度则处于准备运行状态,得到CPU的调度则处于运行状态。在RUNNABLE下,中断只会设置该线程的标志位,不会让线程发生中断,需要程序员自己根据标志位来做一些处理。(比较灵活)
  • BLOCKED:该状态下的线程是由于在竞争某个对象的锁失败而被挂在了该对象的阻塞队列上了。在BLOCKED下,中断也只会设置该线程的标志位,不会让线程发生中断,需要程序员自己根据标志位来做一些处理。
  • WAITING:该状态下的线程处于一直等待状态,需要其他线程调用notify或notifyAll唤醒。在此状态下的线程遇到中断时,会抛出一个InterruptedException的异常,并清空标志位,处理着需要捕获该异常或抛出该异常。
  • TIMED_WAITING:该状态下的线程会在等待一段时间后自动的释放自己,在此状态下的线程遇到中断时,会抛出一个InterruptedException的异常,并清空标志位,处理着需要捕获该异常或抛出该异常。
  • TERMINATED:线程终止状态,此时调用中断不会设置中断标志位(线程都结束了还中断个鬼)

7.1.2:中断策略

1、最合理的中断策略是某种形式的线程级取消操作或服务及取消操作:尽快退出,在必要时进行清理,通知某个所有者该线程已经退出。
2、一个中断请求可以有一个或多个接受者。
3、非线程所有者虽然可以对中断做出响应,但应该保存中断的状态,这样才能使拥有线程的代码对中断做出响应。
4、大多数可阻塞库函数只抛出InterruptedException作为中断响应,也是最合理的取消策略,尽快退出执行流程并把中断信息传递给调用者,从而使栈中的上层代码可以采取进一步的操作。
5、当检查到中断请求时,可以推迟处理中断请求。
6、调用Thread.currentThread().interrupt()来恢复中断状态
7、由于每个线程拥有各自的中断策略,除非知道中断对该线程的含义,否则就不应该中断这个线程。

7.1.3:响应中断

1、当调用可中断的阻塞函数,如Thread.sleep和BlockingQueue.put等,处理InterruptedException的两种策略:

  • 传递异常
  • 恢复中断状态,从而使调用栈的上层代码能对其进行处理

2、只有实现了线程中断策略的代码才可以屏蔽中断请求,在常规的任务和库代码中都不应该屏蔽中断请求。
3、通常可中断的方法会在阻塞或进行重要的工作前首先先检查中断,从而更快的响应中断。
4、如果代码不会调用可中断的阻塞方法,那么仍然可以通过在任务代码中轮询当前线程的中断状态来响应中断。
5、join的不足:无法知道执行控制是因为线程正常退出而返回还是因为join超时而返回。

7.1.5:通过Future来实现取消

1、Future有一个cancel方法,带有一个boolean类型的参数,表示取消操作是否成功。
2、对于那些由于执行不可中断操作而被阻塞的线程,可以使用类似于中断的手段来停止这些线程,但要求知道线程阻塞的原因。
3、当Future.get抛出InterruptedException或TimeoutException时,如果知道不再需要结果,就可以调用Future.cancel来取消任务。

7.1.6:处理不可中断的阻塞

1、许多可阻塞的方法是通过提前返回或抛出InterruptedException来响应中断请求
2、对于执行不可中断操作而被阻塞的线程,可以使用类似于中断的手段来停止这些线程,但这要求我们必须知道线程阻塞的原因。
3、常见的不可中断的阻塞

  • java.io包中的同步Socket I/O
  • java.io包中的同步I/O
  • Selector的异步I/O
  • 获得某个锁

7.2:停止基于线程的服务

1、应用程序准备退出时,服务所拥有的线程也需要退出,并且需要它们自行结束
2、正确的封装原则是,除非拥有某个线程,否则不能对该线程进行操控。
3、线程的所有权是不可传递的。
4、对于持有线程的服务,只要服务的存在时间大于创建线程的方法的存在时间,那么久应该提供生命周期方法。

7.2.2:关闭ExecutorService

  • shutdown:正常关闭,会等正在运行的程序执行完,再关闭线程。速度慢,但安全。
  • shutdownNow:强行关闭,首先关闭当前正在执行的任务,再关闭线程。速度快,但不安全。

7.2.3:“毒丸”对象

1、“毒丸”:一个放在队列上的对象,当得到这个对象时,立即停止。
2、只要当生产者和消费者的数量都已知的情况下,才可以使用毒丸。
3、当生产者和消费者的数量较大时难以使用。
4、只有在无界队列“毒丸”对象才能可靠的工作。
5、“毒丸”可以用来关闭生产者-消费者服务。

7.2.5:shutdownNow的局限性

1、shutdownNow会返回所有已提交但尚未开始的任务,但不会返回已经开始还未结束的任务。

7.3:处理非正常的线程终止

1、导致线程提前死亡的最主要的原因是RuntimeException。
2、每当调用另一个方法时,都要对它的行为保持怀疑,不要盲目地任务它一定会正常返回,或者一定会抛出在方法中声明的某个已检查异常。
3、在运行时间较长的应用程序中,通常会为所有线程的未捕获异常指定同一个异常处理器,并且该处理器至少会将异常信息记录到日志中。

7.4:JVM关闭

JVM既可以正常关闭,也可以强行关闭。

7.4.1:关闭钩子

1、在正常情况下,JVM首先调用所有已注册的关闭钩子。
2、指通过Runtime.addShutdownHook注册的但尚未开始的线程。
3、不应该依赖可能被应用程序或其他关闭钩子关闭的服务,对所有服务使用同一个关闭钩子,各个关闭操作串行执行。

7.4.2:守护线程

1、线程分两种:普通线程和守护线程
2、在JVM启动时创建的所有线程中,除了主线程以外,其他线程都是守护线程
3、应该尽量减少使用守护线程
4、守护线程通常不能用来替代应用程序管理中各个服务的生命周期

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值