Java并发编程实战 第7章 取消与关闭

Java没有提供任何机制来安全地终止线程。但它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的当前工作。这种协作方法是必要的,我们很少希望某个任务、线程或服务立即停止,因为这种立即停止会使共享数据结构处于不一致的状态。在编写任务和服务时可以使用一种协作的方式:当需要停止时,它们首先会清楚当前旨在执行的工作,然后再结束。任务本身比发出取消请求的代码更清楚如何执行清除工作。行为良好的软件能很完善的处理失败、关闭和取消等过程。

7.1 任务取消

在Java中没有一种安全的抢占式方法来停止线程。只有一些协作式的救治,使请求取消的任务和代码都遵循一种协商好的协议。
例如设置某个取消标志,任务定期查看这个标志。为了使这个过程能可靠工作,需要将其设置为volatile类型。

7.1.1 中断

不可靠的取消操作将把生产者置于阻塞的操作中,例如任务:

while (!canelled) {
	queue.put(...);
}

此时生产者速度大于消费者速度,生产者的put方法将被阻塞,消费者希望取消任务,那么会设置cancelled,这时消费者停止消费,生产者保持put阻塞,无法再去检测cancelled标志。

一些特殊的阻塞库的方法支持中断。线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉他在合适的或者可能的情况下停止当前工作,并转而执行其他的工作。

每个线程都有一个boolean类型的中断状态,interrupt能中断目标线程,isInterrupted能返回目标线程的中断状态,interrupted能清除当前线程的中断状态并返回它之前的值。

阻塞库方法,例如Thread.sleep、Object.wait等,都会检查线程合适中断,并在发现中断时提前返回。它们在响应中断时执行的操作包括:清除中断状态,抛出InterruptedException,表示阻塞操作由于中断而提前结束。JVM不能保证阻塞方法检测到中断的速度,但实际上还是非常快的。

对中断操作的正确理解:并不会真正的中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己(取消点)。

通常,中断是实现取消的最合理方式。
如果可中断的阻塞方法的调用频率并不高,不足以获得足够的响应性,那么显示的检测中断状态能起到一定的帮助作用。

7.1.2 中断策略

最合理的中断策略:以某种形式的线程级取消操作或服务级取消操作:尽快退出,在必要时进行清理,通知某个所有者该线程已经退出。

任务不应该对执行该任务的线程的中断策略做出任何假设,除非该任务被专门设计为在服务中运行,并且在这些服务中包含特定的中断策略。

如果除了将InterruptedException传递给调用者外还需要执行其他操作,那么应该在捕获InterruptedException之后恢复中断状态:Thread.currentThread().interrupt();

每个线程拥有各自的中断策略,除非你知道中断对该线程的含义,否则就不应该中断这个线程。线程应该只能由其所有者中断,所有者可以将线程的中断策略信息封装到某个合适的取消机制中,例如shutdown方法。

7.1.3 响应中断

两种实用策略可用于处理InterruptedException:

  • 传递异常 (方法throws出去)从而使方法也成为可中断的阻塞方法
  • 恢复中断状态 (再次调用interrupt来恢复中断)从而使调用栈中的上层代码能够对其进行处理

只有实现了线程中断策略的代码才可以屏蔽中断请求。

如果代码中不会调用可中断的阻塞方法,可以通过任务代码中轮询当前线程的中断状态来响应中断,选择合适的轮询频率是关键。

7.1.4 示例:计时运行

没看懂

7.1.5 通过Future来实现取消

如果任务在被取消前就抛出一个异常,那么该异常将被重新抛出以便由调用者来处理异常。

当Future.get抛出InterruptedException或TimeoutException时,如果你知道不再需要结果,那么就可以调用Future.cancel来取消任务。

7.1.6 处理不可中断的阻塞

再看一遍

7.1.7 采用newTaskFor来封装非标准的取消

写一遍

7.2 停止基于线程的服务

你可能感兴趣的:(并发编程)