一、中断

线程的几种状态:新建、就绪、运行、阻塞、死亡。参考:线程的几种状态转换

线程的可运行状态并不代表线程一定在运行(runnable != running ) 。 大家都知道:所有现代桌面和服务器操作系统都使用了抢占式的线程调度策略 。一旦线程开始执行,并不是总是保持持续运行状态的。当系统分给它的时间片(非常小的运行时间单位)用完以后,不管程序有没有执行完,线程被强制放弃CPU,进入就绪状态,直到下次被调度后开始继续执行。也就是说, Runnable可运行状态的线程处于两种可能的情况下:(1)占用CPU运行中,(2)等待调度的就绪状态。 这里要声明一下:处于等待调度的就绪状态线程和处于阻塞的线程是完全不同的。就绪的线程是因为时间片用完而放弃CPU,其随时都有可能再次获得CPU而运行,这一切取决于分时OS的线程调度策略。

在很多操作系统的专业术语中,这种因时间片用完而被剥夺CPU的情况我们叫做线程中断 。注意这和我们下面要将得中断线程是两个完全不同的概念。事实上,我们不可能通过应用程序来控制CPU的线程中断,除非我们能够自由调用OS的内核。
中断可以理解为线程的一个标识位属性,表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了一个招呼,其他线程通过调用该线程的interrupt()方法对其进行中断操作。

一个正在运行的线程除了正常的时间片中断之外,能否被其他线程控制?或者说其他线程能否让指定线程放弃CPU或者提前结束运行? 除了线程同步机制之外,还有两种方法:
(1) stop(), suspend(), resume() 和Runtime.runFinalizersOnExit() ,但这些方法已经被废弃。
(2) interrupt() 方法

例:创建了一个线程countThread,它不断地进行变量累加,而主线程尝试对其进行中断操作和停止操作。

import java.util.concurrent.TimeUnit;

public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1); //Thread.sleep(1000);
countThread.interrupt();
System.out.println("是否停止1:" + countThread.isInterrupted());
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
two.cancel();
System.out.println("是否停止2:" + countThread.isInterrupted());
} private static class Runner implements Runnable {
private long i; private volatile boolean on = true; @Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
} public void cancel() {
on = false;
}
}
}

运行结果:

是否停止1:true
Count i = 574867187
是否停止2:false
Count i = 581254533

main线程通过中断操作和cancel()方法均可使线程countThread终止。
当我们调用countThread.interrput()的时候,线程countThread的中断状态(interrupted status) 会被置位。我们可以通过Thread.currentThread().isInterrupted() 来检查这个布尔型的中断状态。

在Core Java中有这样一句话:"没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 "。

while循环有一个决定因素就是需要不停的检查自己的中断状态。当外部线程调用该线程的interrupt 时,使得中断状态置位。这时该线程将终止循环,不再执行循环中的内容。

这说明: interrupt中断的是线程的某一部分业务逻辑,前提是线程需要检查自己的中断状态(isInterrupted())。

参考:

java线程之中断线程Interrupted用法

《Java并发编程的艺术》

另:Thread.sleep和TimeUnit.SECONDS.sleep的区别与联系

二、interrupt()、interrupted() 和 isInterrupted()方法的区别

1、interrupt()方法
interrupt()方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。
中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()方法对其进行中断操作。
注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。

2、interrupted() 和 isInterrupted()

public static boolean interrupted() {
return currentThread().isInterrupted(true);
} public boolean isInterrupted() {
return isInterrupted(false);
} private native boolean isInterrupted(boolean ClearInterrupted);

从源代码可以看出:
interrupted()测试的是当前线程(current thread)的中断状态,且这个方法会清除中断状态。是静态方法(它测试的是当前线程的中断状态)
isInterrupted()测试的是调用该方法的对象所表示的线程,且这个方法不会清除中断状态。是实例方法(它测试的是实例对象所表示的线程的中断状态)

关于方法isInterrupted( boolean ClearInterrupted):
通过参数名ClearInterrupted可以知道,这个参数代表是否要清除状态位。
如果这个参数为true,说明返回线程的状态位后,要清掉原来的状态位(恢复成原来情况)。这个参数为false,就是直接返回线程的状态位。

这两个方法很好区分,只有当前线程才能清除自己的中断位(对应interrupted()方法)

例:1. interrupted()方法

 public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000); System.out.println("当前正在执行的线程:" + Thread.currentThread().getName()); thread.interrupt(); // Thread.currentThread().interrupt(); System.out.println("是否停止?=" + thread.interrupted());// false,main线程没有被中断
}
} class MyThread extends Thread {
@Override
public void run() {
int i = 0;
super.run();
for (; i < 500000; i++) {
i++;
}
System.out.println("i: " + i);
}
}

运行结果:

i: 500000
当前正在执行的线程:main
是否停止?=false

第4行启动thread线程,第5行使main线程睡眠1秒钟从而使得thread线程有机会获得CPU执行。
main线程睡眠1s钟后,恢复执行到第7行,请求中断 thread线程。
第11行测试线程是否处于中断状态,这里测试的是哪个线程呢?答案是main线程。因为:

(1)interrupted()测试的是当前的线程的中断状态

(2)main线程执行了第11行语句,故main线程是当前线程

如果将第9行换成Thread.currentThread().interrupt();,则运行结果为:

i: 500000
当前正在执行的线程:main
是否停止?=true

2. isInterrupted()方法

 public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
//Thread.sleep(1000); System.out.println("当前正在执行的线程:" + Thread.currentThread().getName()); thread.interrupt(); System.out.println("是否停止?=" + thread.isInterrupted());// false,main线程没有被中断
}
} class MyThread extends Thread {
@Override
public void run() {
int i = 0;
super.run();
for (; i < 500000; i++) {
i++;
}
System.out.println("i: " + i);
}
}

运行结果:

当前正在执行的线程:main
是否停止?=true
i: 500000

在第11行,是thread对象调用的isInterrupted()方法。因此,测试的是thread对象所代表的线程的中断状态。由于在第9行,main线程请求中断 thread线程,故在第11行的结果为: true

另外:当线程被阻塞的时候,比如被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞时。调用它的interrput()方法。可想而知,没有占用CPU运行的线程是不可能给自己的中断状态置位的。这就会产生一个InterruptedException异常。例:

public class InterruptedSleep {
public static void main(String[] args) {
Runnable tr = new TestRunnable();
Thread th1 = new Thread(tr);
th1.start(); // 开始执行分线程
while (true) {
th1.interrupt(); // 中断这个分线程
}
}
} class TestRunnable implements Runnable {
public void run() {
try {
Thread.sleep(1000000); // 这个线程将被阻塞1000秒
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("是否停止:" + Thread.currentThread().isInterrupted());
}
}
}

运行结果:

java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadArt.TestRunnable.run(InterruptedSleep.java:17)
at java.lang.Thread.run(Thread.java:745)
是否停止:true

因此:

要中断一个Java线程,可调用线程类(Thread)对象的实例方法:interrupte();然而interrupte()方法并不会立即执行中断操作;具体而言,这个方法只会给线程设置一个为true的中断标志(中断标志只是一个布尔类型的变量),而设置之后,则根据线程当前的状态进行不同的后续操作。

1. 如果线程的当前状态处于非阻塞状态,那么仅仅是线程的中断标志被修改为true而已;

2. 如果线程的当前状态处于阻塞状态,那么在将中断标志设置为true后,还会有如下三种情况之一的操作:

(1) 如果是wait、sleep以及jion三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,并抛出一个InterruptedException;
(2) 如果是java.nio.channels.InterruptibleChannel进行的io操作引起的阻塞,则会对线程抛出一个ClosedByInterruptedException;(待验证)
(3) 如果是轮询(java.nio.channels.Selectors)引起的线程阻塞,则立即返回,不会抛出异常。(待验证)
如果在中断时,线程正处于非阻塞状态,则将中断标志修改为true,而在此基础上,一旦进入阻塞状态,则按照阻塞状态的情况来进行处理;例如,一个线程在运行状态中,其中断标志被设置为true,则此后,一旦线程调用了wait、jion、sleep方法中的一种,立马抛出一个InterruptedException,且中断标志被清除,重新设置为false。
通过上面的分析,我们可以总结,调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是:开发人员根据中断标志的具体值,来决定如何退出线程。

参考:
JAVA多线程之中断机制(stop()、interrupted()、isInterrupted())
使用interrupte中断线程的真正用途

对中断interrupt的理解的更多相关文章

  1. 线程中断 interrupt 和 LockSupport

    本文章将要介绍的内容有以下几点,读者朋友也可先自行思考一下相关问题: 线程中断 interrupt 方法怎么理解,意思就是线程中断了吗?那当前线程还能继续执行吗? 判断线程是否中断的方法有几个,它们之 ...

  2. Thread.interrupt()方法理解

    原博客地址: 多线程编程 实战篇 (四) 不客气地说,至少有一半人认为,线程的"中断"就是让线程停止. 如果你也这么认为,那你对多线程编程还没有入门. 在java中,线程的中断(i ...

  3. 对Linux内核中进程上下文和中断上下文的理解

    内核空间和用户空间是操作系统理论的基础之一,即内核功能模块运行在内核空间,而应用程序运行在用户空间.现代的CPU都具有不同的操作模式,代表不同的 级别,不同的级别具有不同的功能,在较低的级别中将禁止某 ...

  4. 理解java线程的中断(interrupt)

    一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果比如会带着自己所持有的锁而永远的休眠,迟迟不归还锁等. 所以你看到Thread.suspend, Threa ...

  5. 日积月累--线程中断interrupt()方法

    线程中断方法interrupt()方法的理解: interrupt()方法的源码: interrupted()方法的源码及注解: isInterrupted()方法源码及注解: 在了解这个方法之前我们 ...

  6. java之线程中断——interrupt

    如下图所示,interrupt()方法并没有成功的中断我们的线程. 为了便于理解,其实可以这样来类比(注意,只是类比,实际情况并不完全是这样):Thread类中有一个boolean的标志域用来表示线程 ...

  7. 51单片机中断interrupt……using……

    51单片机中断细节的一些问题. interrupt0:外部中断0interrupt1:定时器中断0interrupt2:外部中断interrupt3:定时器中断1interrupt4:串口 using ...

  8. 【杂谈】线程中断——Interrupt

    前言 以前有一个错误的认识,以为中断操作都会抛出异常,后来才发现并不是这样,所以今天就来做一个关于中断的总结. 如何关闭线程 已被弃用的Stop方法 早期,Thread类中有一个stop方法,用于强行 ...

  9. 线程的中断.interrupt

    线程对象.interrupt() 注意,异常分析中要有break,否则无法中断 public class Demo extends JFrame { private Thread thread;//定 ...

随机推荐

  1. google sitemap

    引言 刚开始以为要一个绿色快速通道网页,涵盖常用的地址链接,以便于google的爬虫统计数据,然后看了google sitemap站点文档,原来站点地图是一种文件,您可以通过该文件列出您网站上的网页, ...

  2. 团队作业8——敏捷冲刺(Beta阶段)

    Beta阶段--第1篇 Scrum 冲刺博客(计划) https://www.cnblogs.com/just-let-it-go/p/9061664.html Beta阶段--第2篇 Scrum 冲 ...

  3. EBS 请求输出Html报表集成Echarts

    百度开源的ECharts有样式丰富且美观的报表类型可供选用,可以将其集成至EBS请求输出的Html报表中,这其实就是一个生成Html数据的过程. 定义输出类型为HTML的请求我就不在此处赘述.   我 ...

  4. Redis for linux安装配置之—-源码安装

    一‘redis单实例安装配置1.下载redis源码压缩包,并将其上传至服务器/usr/local2.解压redis源码压缩包  # tar -xzvf redis-3.2.12.tar.gz3.进入r ...

  5. vue中提示toFixed不是函数

     vue中toFixed获取小数点后两位 错误提示:.toFixed is not a function解决办法:Number(_this.group_cash).toFixed(2) 转自:http ...

  6. laravel5.5 调用系统自带登陆认证auth

    1执行命令 php artisan make:auth 2 编辑文件 config/auth guardes 'admin' => [ 'driver' => 'session', 'pr ...

  7. 短信猫+kannel调试一例

    同事做一短信网关平台,采用kannel软件. 安装正常,配置文件如下: # Vodafone 3G cardgroup = coreadmin-port = 13000admin-password = ...

  8. 配置阿里云docker镜像地址

    { "registry-mirrors": [ "https://kfwkfulq.mirror.aliyuncs.com", "https://2l ...

  9. jar 包启动

    java -Xms256m -Xmx512m -Xmn256m -jar /home/apps/video/video.jar --spring.profiles.active=test #!/bin ...

  10. Altera FPGA SoC搭建步骤

    Altera SoC 官方搭建指南: https://rocketboards.org/foswiki/Documentation/EmbeddedLinuxBeginnerSGuide 官方文档中除 ...