今天我们聊聊 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() 只是设置了线程的中断标志。

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

// 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

Java 线程的中断机制的更多相关文章

  1. 并发基础(八) java线程的中断机制

    文章转载自 详细分析Java中断机制 1. 引言 当我们点击某个杀毒软件的取消按钮来停止查杀病毒时,当我们在控制台敲入quit命令以结束某个后台服务时--都需要通过一个线程去取消另一个线程正在执行的任 ...

  2. java 线程中断机制

    上一篇文章我们了解过了java有关线程的基本概念,有线程的属性,线程可能处于的状态,还有线程的两种创建的方式,最后还说了一个关键字synchronized,解决了高并发导致数据内容不一致问题,本篇文章 ...

  3. java 多线程6: 中断机制 优雅的终止java线程

    前文 java 多线程5: java 终止线程及中断机制 (stop().interrupt() .interrupted().isInterrupted()) 使用 interrupt() 和 in ...

  4. 【转载】 Java线程面试题 Top 50

    Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...

  5. Java线程面试题 Top 50 (转载)

    转载自:http://www.cnblogs.com/dolphin0520/p/3958019.html 原文链接:http://www.importnew.com/12773.html   本文由 ...

  6. 50 道 Java 线程面试题(转载自牛客网)

    下面是 Java 线程相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理 ...

  7. Java线程面试题 Top 50

    转自:http://www.importnew.com/12773.html 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java ...

  8. 【多线程】Java线程面试题 Top 50(转载)

    Java线程面试题 Top 50 原文链接:http://www.importnew.com/12773.html   本文由 ImportNew - 李 广 翻译自 javarevisited.欢迎 ...

  9. Java线程面试题 Top 50(转载)

    原文链接:http://www.importnew.com/12773.html 本文由 ImportNew - 李 广 翻译自 javarevisited.欢迎加入Java小组.转载请参见文章末尾的 ...

随机推荐

  1. POJ - 3662 Telephone Lines (Dijkstra+二分)

    题意:一张带权无向图中,有K条边可以免费修建.现在要修建一条从点1到点N的路,费用是除掉免费的K条边外,权值最大的那条边的值,求最小花费. 分析:假设存在一个临界值X,小于X的边全部免费,那么此时由大 ...

  2. C++中的config设计

    配置文件读写类,它要有以下这些方法: 1. 支持读入一个指定配置文件的能力 2. 支持随时加入一个配置项的能力 3. 足够强大,能够写入各种数据结构的配置信息 C++ 里,我们要存储这样的数据就使用 ...

  3. launch 文件解析

    roslaunch工具是ros中python实现的程序启动工具,通过读取launch文件中的参数配置.属性配置等来启动一系列节点: 很多ROS包或源码包中都有launch文件,一般为该程序包能够运行起 ...

  4. LigerUI v1.2.4 LigerGrid默认样式 工具条背景白色

    修改Aqua的ligerui-grid.css .l-panel-topbar 样式 修改为: .l-panel-topbar{padding: 0;background: #CEDFEF url(' ...

  5. MySQL-5.7创建及查看数据库

    1.创建数据库语句 create database语句是在MySQL实例上创建一个指定名称的数据库. create schema语句的语义和create database是一样的. 2.语法解析 CR ...

  6. 20145240 GDB调试汇编堆栈过程分析

    20145240 GDB调试汇编堆栈过程分析 测试代码 #include<stdio.h> short addend1 = 1; static int addend2 = 2; const ...

  7. 2062326 齐力锋 实验四《Java面向对象程序设计Android开发》实验报告

    北京电子科技学院(BESTI) 实 验 报 告 课程: 程序设计与数据结构  班级: 1623  姓名: 齐力锋 学号: 20162326 成绩: 指导教师: 娄嘉鹏/王志强 实验日期: 2017年5 ...

  8. Oracle 伪列

    ROWNUM ROWNUM:表示行号,实际上此是一个列,但是这个列是一个伪列,此列可以在每张表中出现. 范例:在查询雇员表上,加入 ROWNUM SELECT ROWNUM,empno,ename,j ...

  9. Caffe2——C++ 预测(predict)Demo

    因为最近入坑Caffe2,它最近还一直在更新,所以坑比较多,官方也只给出了python的demo,C++的暂时还找不到,有也只有一个简单版的,不够用,所以就总结了一下,结合网上和自己的实践,整理了一下 ...

  10. spring cloud 基本小结

    Spring cloud系列十四 分布式链路监控Spring Cloud Sleuth https://blog.csdn.net/hry2015/article/details/78905489 S ...