java多线程中断机制,Java 线程的中断机制

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

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

将线程从阻塞等待中唤醒,并作出相应的“受控中断”处理。

尝试告知目标线程:请打断现有处理流程,响应新的命令。

以第一种用途为例,请看以下代码:

synchronized (lock) {

try {

while (!check()) {

lock.wait(1000);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

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

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

另一个线程执行下述代码主动唤醒 synchronized (lock) {

lock.notifyAll(); // or lock.notify();

}

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

另一个线程要求等待的线程“中断” // 拿到等待中的线程的引用

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 留给开发者这一自由,我们应当予以善用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值