一.Executor生命周期
1.线程池有运行、关闭、停止、结束四种状态,结束后就会释放所有资源
2.shutdown 与 shutdowNow 分别对应
- 平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务
- 立即关闭:取消所有正在执行和未执行的任务
3.检测线程池是否正处于关闭中,使用isShutdown()
描述的是非RUNNING状态,也就是SHUTDOWN/STOP/TERMINATED三种状态
4.检测线程池是否已经关闭使用isTerminated()
描述的是关闭状态,也就是TERMINATED三种状态
5.定时或者永久等待线程池关闭结束使用awaitTermination()操作
shutdown 与 shutdowNow不是阻塞操作,只是发起关闭任务,awaitTermination则是等待到线程isTerminated()
二.任务生命周期
任务定义了7种状态:
- NEW:新建
- COMPLETING:完成
- NORMAL:正常运行
- EXCEPTIONAL:异常退出
- CANCELLED:任务取消
- INTERRUPTING:线程中断中
- INTERRUPTED:线程已中断
在这七种状态中,有四种任务终止状态:NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED。
以下为7种状态的关系情况
- NEW—>COMPLETING—>NORMAL(任务执行正常)
- NEW—>COMPLETING—>EXCEPTIONAL(任务执行异常)
- NEW—>CANCELLED(等待任务的取消)
- NEW—>INTERRUPTING—>INTERRUPTED(执行中任务的取消)
JAVA中,Future 表示了一个任务的生命周期。
Future.get()
如果任务正常执行,则返回任务结果
如果任务执行异常,则会抛出执行异常
如果任务被取消,则抛出取消异常(末执行任务直接不执行,正在执行任务中断)
中断正在执行的任务
采用interrupt()。对线程设置为中断标志。设置后不会立刻中断线程,而是待调用严格检查异常interruptException的时候就会停止,比如await,sleep等。
然而,并不是所有的阻塞都会响应中断,对于不能响应中断interruptException的阻塞。interrupt方法只能标志位可中断,但是无任何其他的作用。处理不可中断的阻塞 ,我们应该查找阻塞的原因,然后重写interrupt的方法来中断。
1.io包中的socket I/O:阻塞I/O形式为对套接字的read、write操作。虽然read,write操作不会响应中断,但是通过关闭底层的套接字,可以使得由于使用该方法而阻塞的线程抛出socketException
2.io包中的同步I/O
3.selector的异步IO,close或wakeup的时候会使线程抛出CloseSelectorException
4.获得某个锁:如果一个线程因等待某个锁而阻塞,那么该线程是不会理会中断,唯一的办法是使用实现lock接口的reentrantlock(注释3)对象
(具体参考JAVA并发编程实战)
Future的灵活运用
等待一任务超过一定时间则取消它
public static void timeRun(Runnable r, long timeout, TimeUnit unit){
ExecutorService executor = Executors.newSingleThreadExecutor();
Future task = executor.submit(r);
try {
task.get(timeout, unit);
} catch (InterruptedException e) {
//中断取消
} catch (ExecutionException e) {
//执行异常
} catch (TimeoutException e) {
//超时
}finally {
//如果任务超时,则直接取消,结果已没作用
task.cancel(true);
//true 不管任务是否执行,可以中断。false:如果任务还未执行,就不要运行。
}
}