Java 线程的中断机制


声明:本文转载自https://my.oschina.net/tridays/blog/1587259,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

今天我们聊聊 Java 线程的中断机制。

线程中断机制提供了一种方法,用于将线程从阻塞等待中唤醒,并作出相应的“受控中断”处理。

synchronized (lock) {     try {         while (!check()) {             lock.wait(1000);         }     } catch (InterruptedException e) {         e.printStackTrace();     } } 

这段代码使用了 Java 提供的 wait/notify 机制,线程执行 lock.wait() 会阻塞,有三种情况使线程恢复运行。

  1. 超时 1000ms 结束,正常执行下一句代码。

  2. 另一个线程执行下述代码主动唤醒

  3. 另一个线程执行下述代码主动唤醒

    synchronized (lock) {     lock.notifyAll(); // or lock.notify(); } 

    这也会正常执行下一句代码。

  4. 另一个线程要求等待的线程“中断”

    // 拿到等待中的线程的引用 Thread a; a.interrupt(); 

    被“中断”的线程 a,会在 lock.wait() 处抛出 InterruptedException 异常。

综上所述,你可以认为 object.wait() 内部在做这些事:

boolean checkTimeout = timeout > 0; Thread current = Thread.currentThread(); lock.addWaiter(current); while (!current.isNotified()) {     if (current.isInterrupted()) {         current.clearInterrupted();         throw new InterruptedException();     }     if (checkTimeout) {         if (timeout == 0) break;         timeout--;     } } 

这不完全准确,因为 wait 不使用这种“忙轮询”的方式做检查,但关于标志位的判断逻辑是正确的。

让我们从手动中断开始探究,

// sun.nio.ch.Interruptible  public interface Interruptible {     void interrupt(Thread var1); }  // java.lang.Thread  private volatile Interruptible blocker; private final Object blockerLock = new Object();  public void interrupt() {     if (this != Thread.currentThread())         checkAccess();      synchronized (blockerLock) {         Interruptible b = blocker;         if (b != null) {             interrupt0();             b.interrupt(this);             return;         }     }     interrupt0(); }  // Just to set the interrupt flag private native void interrupt0(); 

能够看出,thread.interrupt() 先判断权限,然后实际调用 interrupt0() 设置线程的中断标志,如果当前线程有 nio 的 Interruptible 那么还会回调它。

注意,interrupt0() 只是设置了线程的中断标志。

一个线程怎么知道自己被打断了?

// java.lang.Thread  public static boolean interrupted() {     return currentThread().isInterrupted(true); }  public boolean isInterrupted() {     return isInterrupted(false); }  private native boolean isInterrupted(boolean clearInterrupted); 

也就是说,isInterrupted(boolean) 会返回线程是否被打断,并根据需要清空中断标志。

我们发现,当一个线程并不阻塞,没有在 object.wait(), thread.join(), Thread.sleep() 等不受 Java 程序逻辑控制的区域时,那么线程是否被打断只能通过检查中断标志得知。

当一个函数调用可能阻塞,Java 会在阻塞的源头签名里标记 throws InterruptedException,并要求编写 try catch 处理中断。

当线程阻塞,就像上文所述,Java 检查到中断标志,先将其清除,然后抛出 InterruptedException

// java.lang.Object  public final void wait() throws InterruptedException {     wait(0); }  public final native void wait(long timeout) throws InterruptedException; 

如果一个线程收到 InterruptedException,之后仍然执行了会引发阻塞的代码,它将像“没事人”一样继续阻塞住。因为 Java 在内部将中断标志清除了!

我们常见地编写以下三类处理 InterruptedException 的代码:

InterruptedException 交由上层处理。

public void foo() throws InterruptedException {     synchronized (lock) {         lock.wait();     } } 

遇到 InterruptedException 重设中断标志位。

try {     synchronized (lock) {           lock.wait();       }   } catch (InterruptedException e) {       Thread.currentThread().interrupt();     //break;  }  

先忙完,再重新抛出 InterruptedException

public void bar() throws InterruptedException {     InterruptedException ie = null;     boolean done = false;     while (!done) {         synchronized (lock) {             try {                 lock.wait();             } catch (InterruptedException e) {                 ie = e;                 continue;             }         }         done = true;     }     if (ie != null) {         throw ie;     } } 

如果一个线程无视中断标志和 InterruptedException,它仍然能够跑的很好。但这与我们设计多线程的初衷是违背的,我们希望线程之间是和谐的有序协作以实现特定功能,因此受控线程应当对中断作出响应。而 Java 留给开发者这一自由,我们应当予以善用。

本文发表于2017年12月10日 10:33
(c)注:本文转载自https://my.oschina.net/tridays/blog/1587259,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1983 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1