什么是中断?

在 Java 中没有办法立即停止一个线程,然而停止线程却显得尤为重要,如提前终止一个比较耗时的操作。因此,Java 提供了一种用于停止线程的机制 - 中断。

中断只是一种协作机制,Java 没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的 interrupt 方法,该方法也仅仅是将线程对象的中断标识设成true;接着你可以调用 interrupted 方法检测当前线程的标识位;如果为true,表示别的线程要求这条线程中断,此时究竟该做什么需要你自己写代码实现。

每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为 true 表示中断,为 false 表示未中断;

通过调用线程对象的 interrupt 方法将该线程的标识位设为 true ;可以在别的线程中调用,也可以在自己的线程中调用。

另外,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。而 Thread.interrupt 的作用其实也不是中断线程,而是「通知线程应该中断了」,具体到底中断还是继续运行,应该由被通知的线程自己处理。

案例

  1. public class Main {
  2. public static void main(String[] args) {
  3. Ticket ticket = new Ticket();
  4. TicketingWindow window1 = new TicketingWindow(ticket);
  5. TicketingWindow window2 = new TicketingWindow(ticket);
  6. TicketingWindow window3 = new TicketingWindow(ticket);
  7. window1.setName("1号售票窗口");
  8. window2.setName("2号售票窗口");
  9. window3.setName("3号售票窗口");
  10. window1.start();
  11. window2.start();
  12. window3.start();
  13. // 告知窗口 2 可以停止售票了
  14. window2.interrupt();
  15. try {
  16. Thread.sleep(2000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. window1.StopSelling();
  21. }
  22. }
  23. class Ticket {
  24. public int stock = 5;
  25. }
  26. class TicketingWindow extends Thread {
  27. public TicketingWindow(Ticket ticket) {
  28. this.ticket = ticket;
  29. }
  30. private Ticket ticket;
  31. private boolean status = true;
  32. @Override
  33. public void run() {
  34. while (ticket.stock > 0 && status) {
  35. try {
  36. // 每隔 20 秒卖出一张票
  37. System.out.println(this.getName() + "休眠中...");
  38. sleep(2000);
  39. } catch (InterruptedException e) {
  40. System.out.println(this.getName() + "提前停止服务");
  41. return;
  42. }
  43. System.out.println(this.getName() + ":" + --ticket.stock);
  44. }
  45. System.out.println(this.getName() + "停止服务");
  46. }
  47. public void StopSelling() {
  48. System.out.println(this.getName() + "即将提前停止服务");
  49. this.status = false;
  50. }
  51. }

补充

具体来说,当对一个线程,调用 interrupt() 时,
① 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。
② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。

interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。
① 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
② 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)

  1. Thread thread = new Thread(() -> {
  2. while (!Thread.interrupted()) {
  3. // do more work.
  4. }
  5. });
  6. thread.start();
  7. // 一段时间以后
  8. thread.interrupt();