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() 只是设置了线程的中断标志。
一个线程怎么知道自己被打断了?
// 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 留给开发者这一自由,我们应当予以善用。如果你想学习java可以加我的学习群669823128
最新文章
- 微信小程序-表单组件
- 常用jQuery代码02
- Android开源项目(转载)
- WCF 项目应用连载[3] - 双向通信 实例管理与服务端监控
- 自己写的简单的jQuery分页控件
- 改进的简单Tooltips显示
- 机房收费系统之vb报表的模板的制作(一)
- android端向服务器提交请求的几种方式
- Erlang OTP gen_event
- (三)Python运算符
- ssh无密码登录多台机器,并让所有远程机执行相同命令
- 算法复杂度中的O(logN)底数是多少
- 用Qt程序打开.txt 文件的时候,出现乱码的情况
- JacobMathType
- 【Shell】带颜色输出(白底x色)
- mybatis学习 十六 auto_mapping实现连表查询
- PLSQL Developer新手使用教程(图文教程)
- C++之程序时间统计类实现
- 解决 SSH 不能输入中文的问题
- HDU1717--小数化分数2