欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅析Java线程的中断机制

程序员文章站 2024-02-14 16:35:58
线程中断机制提供了一种方法,用于将线程从阻塞等待中唤醒,尝试打断目标线程的现有处理流程,使之响应新的命令。java 留给开发者这一*,我们应当予以善用。 今天我们聊聊...

线程中断机制提供了一种方法,用于将线程从阻塞等待中唤醒,尝试打断目标线程的现有处理流程,使之响应新的命令。java 留给开发者这一*,我们应当予以善用。
今天我们聊聊 java 线程的中断机制。

线程中断机制提供了一种方法,有两种常见用途:

将线程从阻塞等待中唤醒,并作出相应的“受控中断”处理。
尝试告知目标线程:请打断现有处理流程,响应新的命令。
以第一种用途为例,请看以下代码:

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

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

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

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

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

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

3、另一个线程要求等待的线程“中断”

// 拿到等待中的线程的引用
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() 只是设置了线程的中断标志。

当一个线程并不阻塞,没有在 object.wait(), thread.join(), thread.sleep() 等不受 java 程序逻辑控制的区域时,那么会发生什么事情?答案是不会发生任何事情,线程是否被打断只能通过主动地检查中断标志得知。

怎么检查?thread 暴露了两个接口,thread.interrupted() 和 thread.isinterrupted()。

// 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),而它会返回线程是否被打断,并根据需要清空中断标志。

当一个函数调用会发生阻塞,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 留给开发者这一*,我们应当予以善用。

以上就是这次给大家介绍的java线程的中断机制相关知识的全部内容,如果还有任何不明白的可以在下方的留言区域讨论,感谢对的支持。