Crab213's Blog.

Java并发编程实践笔记5:取消与关闭

2016/04/02

大多数情况下我们想让线程中的任务被完整执行,但是还是有例外的情况。对于有些任务,我们可能在它执行完毕之前就可能会发现它的结果已经不需要了,这时我们就希望有能提前终止任务的手段。在java中想要安全快速可靠地终止一个任务不适什么容易的事情,java语言本身没有提供安全地让线程强制停止的手段。相反,它提供了一种协同机制,让一个线程请求另一个线程,让其停止它所做的事情。

任务取消(Task Cancellation)

如果一个外部代码让一个活动提前结束,我们称这个活动是可以取消的。有很多原因会导致我们想要取消一个活动:

  • 用户请求取消。比如应用程序的用户需要让程序取消某一操作。
  • 时限。有些操作可能被严格限制了运行时间
  • 错误。比如系统发生了某些错误导任务无法继续进行。
  • 关闭。应用程序关闭时会要求所有正在跑的线程退出,否则就无法正常关闭。

java没有强制安全关闭线程的手段,自然也没有安全地强行终止任务的手段。我们只有使用协作的方式来安全停止任务。简单来说,我们可以使用一个volatile变量来保存一个标志,正在运行的任务可以定期检查这个标志位,一旦检测到标志位被设置,就自己停止任务,并退出。

如果想要实现一个可以被取消的任务,必须考虑“how”,“when”,“what”:

  • how 其他代码如何告知这个任务需要被取消
  • when 这个任务什么时候会检测取消请求
  • what 当收到取消请求后,采取什么行动

中断(Interruption)

任务在接到取消请求后可能需要一定时间处理后事,也就是说不会立即退出,也存在任务内部操作发生阻塞而不能及时响应取消请求的情况。所以,使用标志位的方法也不是该问题的最优解。线程中断(thread interruption)是一种让一个线程告知其他线程它该终止了的协作机制。虽然没有任何地方将线程中断和任务取消联系在一起,但是在实际应用中,不使用线程中断的任务取消往往是脆弱且难以维护的。

每一个线程都有一个boolean标志表示其的中断状态。如果线程是中断的,这个标志位被设为true

1
2
3
4
5
6
7
8
public class Thread {
public void interrupt() { ... }
private native boolean isInterrupted(boolean ClearInterrupted);
public boolean isInterrupted() { return isInterrupted(false); }
public static boolean interrupted() {
return currentThread().isInterrupted(ture);
}
}

interrupt方法会中断对应线程,isInterrupted返回对应线程的中断标志位。interrupted这个方法会清除当前线程的中断标志位并返回先前的标志,这时唯一的清除中断标志位的方式。

阻塞的java标准库方法如Thread.sleepObject.wait会试着检测这个中断标志,如果这个标志被设置了,那么它们会清除这个标志,抛出InterruptedException异常,表示线程的不正常退出。JVM不会保证这些阻塞方法会快速的检测到标志位的改变,但是在实际中是很快的。

如果我们没有在跑跑上面那些阻塞方法,我们不会得到InterruptedException异常,我们需要自己轮询检测这个标志位是否被设置。就像上面的自设标志一样,调用interrupt方法并不会直接终止线程,只是会简单告知线程它应该终止了。

线程中断通常是实现任务取消最明智的方式。

CATALOG
  1. 1. 任务取消(Task Cancellation)
  2. 2. 中断(Interruption)