取消和关闭
为什么良好的处理取消和关闭很重要?
感觉苹果就是取消机制做的好,所以用起来很舒服
任务取消
取消的含义?
在任务正常完成之前停止
何时会触发“取消”?
- 用户请求取消
- 一定时间内未完成则取消
- 应用程序事件:多任务搜索,其中一个搜索成功,其余的任务就取消
- 错误:多任务爬虫,磁盘满了,所有任务取消并记录当前状态
- 关闭:程序或服务停止,必须对正在执行和等待执行的工作进行取消操作。
如何理解“java中没有安全的抢占式方式来停止线程”?
抢占式方式会带来数据不一致的问题。
如何实现取消?
设置“取消”标记,定期检查
上述实现有哪些问题?
当调用中断方法时,可能被阻塞,无法进行后续的检查。
如何解决上述问题?
中断
而对于中断方法,interrupt()可以跳出阻塞,但是需要注意的是:
中断有两个结果:
- 如果不是阻塞方法,仅仅将中断状态置为true
- 如果中断阻塞方法,将抛出InterruptException,同时中断状态置为false
中断策略
与任务有取消策略,线程也有中断策略,决定how,when,what
一般情况下,发生中断时,任务也会取消,因此通常,中断是实现取消最合理的方式。
中断时取消
中断和取消是两个概念,我们要区分”在中断发生时,顺便取消一下任务“和“使用中断实现取消任务”的区别。
中断时,任务也可以选择不取消,但是在任务完成时需要恢复中断
如何在中断时取消?
在任务中使用中断状态作为“取消”标记,用isInterrupted判断是否“取消”
中断时取消任务有两个处理:
- 对于任务而言,由取消策略处理
- 对于线程而言,由中断策略处理
任务和线程是两个概念,多任务也可以在单线程中执行。
在取消策略中处理了“中断状态”后,一定要将其保留,提供给中断策略处理
在任务中响应中断的方式如下:
- 捕获InterruptException后重新抛出非检查异常
- 捕获InterruptException后重新调用interrupt()恢复中断状态
任务对于线程的所有者(如线程池)如何响应中断是未知的,所以一定不能“生吞”中断
使用中断实现取消
因为我们目的是取消任务,而不是发生中断才去取消任务,那么能否直接用中断来实现任务的取消?
在中断策略前提下由中断策略决定
中断策略未知的情况下,使用中断来取消某个任务,可能会导致其它所有任务都取消了。
常见的两个可实现场景:
场景1:单线程单任务,一般用thread.interrupt()来取消
场景2:标准线程池(Exceutor框架下),配合Future的cacel()方法
取消由任务所有者发起,中断由线程所有者发起。
线程池中如何进行任务的取消?
如果是使用标准的Exceutor框架里的线程池,其中断策略是
如果池正在停止,请确保线程被中断;
如果没有,请确保线程没被中断(中断状态会恢复)。
如果任务异常,则该线程结束,另启一个新的代替
了解到其中断策略后,可以使用中断来实现取消,最好的方式是配合Future的cacel()方法。
处理不可中断的阻塞
不可中断(innterrupt)的堵塞有:
- java.io包中的同步Socket I/O
- java.io包中的同步I/O
- Selector的异步I/O
- 获取某个锁
但是都会有对应的其他中断机制,这些情形下如何取消任务?
使用上诉的中断机制重写(在中断可以实现取消的情形下):
场景1:重写interrupt方法
场景2:重写FutureTask的cancel()
总结:
- 发生中断时取消任务和使用中断取消任务是两种情况
- 使用中断取消任务场景场景是单线程单任务和线程池配合Future(都属于中断策略已知)
停止基于线程的服务
日志服务
存在问题:不支持取消
优化:如下
存在问题:安全问题
优化:如下
关闭ExecutorService
“毒丸”对象
只执行一次的服务
shutdownNow的局限性
无法知道关闭时,哪些任务正在执行
处理非正常的线程终止
在多线程的情况下,主线程无法捕捉到其他异常信息。有如下解决方案:
- 当一个线程由于未捕获异常即将终止时,Java虚拟机将使用thread . getuncaughtexceptionhandler()查询线程的uncaughtException处理程序,并调用处理程序的uncaughtException方法,将线程和异常作为参数传递。
- 如果希望任务抛出的异常做相应的处理,标准线程池中提供afterExecute()供改写
1 | try { |
线程池中只有execute()涉及上述处理,submit()会将异常保存下来,get时获取
JVM关闭
关闭钩子
关闭钩子有何用?
在jvm正常关闭时,执行一些关闭操作(如上述的日志服务关闭)
由于日志线程会一直执行,想正常关闭只能走其它三种方式
关闭钩子如果无法正常完成?
会陷入死循环,无法正常关闭。
如何解决关闭钩子的并发问题?
尽量将所有逻辑写在一个关闭钩子中,避免并发安问题。
守护线程
守护线程和普通线程的区别?
当一个线程退出时,JVM会检查是否存在其它非守护线程,如果不存在,则正常退出。
终结器
避免使用终结器,可以用finnally代码块和显示的close方法代替来其它资源。