Java面试-interrupt
我们都知道,Java中停止一个线程不能用stop,因为stop会瞬间强行停止一个线程,且该线程持有的锁并不能释放。大家多习惯于用interrupt,那么使用它又有什么需要注意的呢?
interrupt相关的方法
Java中和interrupt相关的方法有三个
public boolean isInterrupted()
public void interrupt()
public static boolean interrupted()
boolean isInterrupted()
每个线程都一个状态位用于标识当前线程对象是否是中断状态。isInterrupted主要用于判断当前线程对象的中断标志位是否被标记了,如果被标记了则返回true,表示当前已经被中断,否则返回false。我们也可以看看它的实现源码:
public boolean isInterrupted() {
return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);
底层调用的native方法isInterrupted,传入一个boolean类型的参数,用于指定调用该方法之后是否需要清除该线程的中断标识位。从这里我们也可以看出来,调用isInterrupted()并不会清除线程的中断标识位。
void interrupt()
interrupt()用于设置当前线程对象的中断标识位,其源码为:
public void interrupt() {
// 检查当前线程是否有权限修改目标线程,如果没有,则会抛出异常SecurityException
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
blockerLock和blocker都和阻塞IO时产生的中断相关,因此推测interrupt()需要当阻塞IO操作执行完之后,才可以执行。
interrupt()其实只是改变了一个标志位,对于线程本身的状态并没有影响。
boolean interrupted()
该方法是一个静态的方法,用于返回当前线程是否被中断,其源码是:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
需要注意的是:该方法调用结束的时候会清空中断标识位。
线程的状态与中断的关系
我们知道,Java中的线程一共6种状态,分别是NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED(Thread类中有一个State枚举类型列举了线程的所有状态)。下面我们就将把线程分别置于上述的不同种状态,然后看看中断操作对它们的影响。
NEW和TERMINATED
NEW状态表示线程还未调用start()方法,TERMINATED状态表示线程已经运行终止。
这两个状态下调用中断方法来中断线程的时候,Java认为毫无意义,所以并不会设置线程的中断标识位。例如:
NEW状态:
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println(thread.getState());
System.out.println(thread.isInterrupted());
thread.interrupt();
System.out.println(thread.isInterrupted());
}
输出结果:
NEW
false
false
TERMINATED状态:
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread();
// 开始线程
thread.start();
// 等待线程结束
thread.join();
System.out.println(thread.getState());
System.out.println(thread.isInterrupted());
thread.interrupt();
System.out.println(thread.isInterrupted());
}
输出结果:
TERMINATED
false
false
从上述的两个例子来看,处于NEW和TERMINATED状态的线程,对于中断是屏蔽的,也就是说中断操作对这两种状态下的线程是无效的。
RUNNABLE
处于RUNNABLE状态的线程,当中断线程后,会修改其中断标志位,但并不会影响线程本身。例如:
/**
* 自定义线程类
*/
public class MyThread extends Thread{
@Override
public void run(){
while(true){
// 什么都不做,就是空转
}
}
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
System.out.println(thread.getState());
System.out.println(thread.isInterrupted());
thread.interrupt();
System.out.println(thread.isInterrupted());
System.out.println(thread.getState());
}
}
结果为:
RUNNABLE
false
true
RUNNABLE
中断标志位确实被改变了,但线程依旧继续运行。那我们调用interrupt()方法的意义在哪儿?
其实Java是将中断线程的权利交给了我们自己的程序,通过中断标志位,我们的程序可以通过boolean isInterrupted()方法来判断当前线程是否中断,从而决定之后的操作。
我们可以在此基础上,保证执行任务的原子性。例如修改MyThread类的方法:
/**
* 自定义线程类
*/
public class MyThread extends Thread{
@Override
public void run(){
while(true){
if (this.isInterrupted()){
System.out.println("exit MyThread");
break;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
System.out.println(thread.getState());
System.out.println(thread.isInterrupted());
thread.interrupt();
System.out.println(thread.isInterrupted());
thread.join();
System.out.println(thread.getState());
}
}
结果为:
RUNNABLE
false
true
exit MyThread
TERMINATED
BLOCKED
当线程处于BLOCKED状态,说明该线程由于竞争某个对象的锁失败而被挂在了该对象的阻塞队列上了。
那么此时发起中断操作不会对该线程产生任何影响,依然只是设置中断标志位。例如:
/**
* 自定义线程类
*/
public class MyThread extends Thread{
public synchronized static void doSomething(){
while(true){
// 空转
}
}
@Override
public void run(){
doSomething();
}
public static void main(String[] args) throws InterruptedException {
// 启动两个线程
Thread thread1 = new MyThread();
thread1.start();
Thread thread2 = new MyThread();
thread2.start();
Thread.sleep(1000);
System.out.println(thread1.getState());
System.out.println(thread2.getState());
System.out.println(thread2.isInterrupted());
thread2.interrupt();
System.out.println(thread2.isInterrupted());
System.out.println(thread2.getState());
}
}
结果为:
RUNNABLE
BLOCKED
false
true
BLOCKED
thread2处于BLOCKED状态,执行中断操作之后,该线程仍然处于BLOCKED状态,但是中断标志位却已被修改。
这种状态下的线程和处于RUNNABLE状态下的线程是类似的,给了我们程序更大的灵活性去判断和处理中断。
WAITING/TIMED_WAITING
这两种状态本质上是同一种状态,只不过TIMED_WAITING在等待一段时间后会自动释放自己,而WAITING则是无限期等待,需要其他线程调用类似notify方法释放自己。但是他们都是线程在运行的过程中由于缺少某些条件而被挂起在某个对象的等待队列上。
当这些线程遇到中断操作的时候,会抛出一个InterruptedException异常,并清空中断标志位。例如:
/**
* 自定义线程类
*/
public class MyThread extends Thread{
@Override
public void run(){
synchronized (this){
try {
wait();
} catch (InterruptedException e) {
System.out.println("catch InterruptedException");
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
Thread.sleep(1000);
System.out.println(thread.getState());
System.out.println(thread.isInterrupted());
thread.interrupt();
Thread.sleep(1000);
System.out.println(thread.isInterrupted());
}
}
结果为:
WAITING
false
catch InterruptedException
false
从运行结果看,当线程启动之后就被挂起到该线程对象的等待队列上,然后我们调用interrupt()方法对该线程进行中断,输出了我们在catch中的输出语句,显然是捕获了InterruptedException异常,接着就看到该线程的中断标志位被清空。
因此我们要么就在catch语句中结束线程,否则就在catch语句中加上this.interrupt();,再次设置标志位,这样也方便在之后的逻辑或者其他地方继续判断。
总结
我们介绍了线程在不同状态下对于中断请求的反应:
NEW和TERMINATED对于中断操作几乎是屏蔽的。RUNNABLE和BLOCKED类似,对于中断操作只是设置中断标志位并没有强制终止线程,对于线程的终止权利依然在程序手中。WAITING和TIMED_WAITING状态下的线程对于中断操作是敏感的,他们会抛出异常并清空中断标志位。
有兴趣的话可以关注我的公众号或者头条号,说不定会有意外的惊喜。


Java面试-interrupt的更多相关文章
- Java面试官最常问的volatile关键字
在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯 ...
- Java面试 32个核心必考点完全解析
目录 课程预习 1.1 课程内容分为三个模块 1.2 换工作面临问题 1.3 课程特色 课时1:技术人职业发展路径 1.1 工程师发展路径 1.2 常见技术岗位划分 1.3 面试岗位选择 1.4 常见 ...
- Java面试知识点之线程篇(三)
前言:这里继续对java线程相关知识点进行总结,不能间断. 1.yield()方法 yield()的作用是让步.它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执 ...
- 最近5年183个Java面试问题列表及答案[最全]
Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别(String 类型和 StringBuffer 类型的主要性能区别其实在于 Stri ...
- 转 最近5年183个Java面试问题列表及答案[最全]
Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别(String 类型和 StringBuffer 类型的主要性能区别其实在于 Stri ...
- 慕课网:剑指Java面试-Offer直通车视频课程
慕课网:剑指Java面试-Offer直通车视频课程,一共有10个章节. 目录结构如下: 目录:/2020036-慕课网:剑指Java面试-Offer直通车 [6G] ┣━━第10章 Java常用类库与 ...
- Java面试宝典2017
JAVA面试.笔试题(2017版) 欲想成功,必须用功! 目录 一. HTML&CSS部分................ ...
- JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结
我是一名java开发人员,hibernate以及mybatis都有过学习,在java面试中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解和 ...
- 转:最近5年133个Java面试问题列表
最近5年133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...
随机推荐
- application.yml 增加数据库连接,重启日志卡死
SpringBoot引入JPA,application.ymlapplication.yml增加数据库链接参数,启动卡死,日志没有动,如下图 折腾好久,后面发现用 Maven的package 过程中 ...
- python聚类算法解决方案(rest接口/mpp数据库/json数据/下载图片及数据)
1. 场景描述 一直做java,因项目原因,需要封装一些经典的算法到平台上去,就一边学习python,一边网上寻找经典算法代码,今天介绍下经典的K-means聚类算法,算法原理就不介绍了,只从代码层面 ...
- Windows上IDEA搭建最新Spark2.4.3源码调试的开发环境
相信很多同学都想通过阅读一些框架的源码,来提高自己的代码能力,但往往在第一步,搭建环境的时候就碰了壁. 本篇就来介绍下如何在Windows下,将最新版的Spark2.4.3编译,并导入到IDEA编译器 ...
- ionic 删除返回按钮文字,及自定义nav两侧的按钮和title格式
1.删除返回按钮文字 1)可以在app.js进行配置 .config(function($stateProvider, $urlRouterProvider,$ionicConfigProvider) ...
- 学测试,看视频?NONONO,除非这种情况
001 前言 : 很久没周末写文章了,一个是要睡懒觉.另外一个是,周末写了大家也没有心思看(加班1周了,好不容易周末,你又让我学习 ?先睡个懒觉再说,去TM的学习). 然而,今天早早的5点就起床了,处 ...
- Leetcode之深度优先搜索(DFS)专题-1080. 根到叶路径上的不足节点(Insufficient Nodes in Root to Leaf Paths)
Leetcode之深度优先搜索(DFS)专题-1080. 根到叶路径上的不足节点(Insufficient Nodes in Root to Leaf Paths) 这篇是DFS专题的第一篇,所以我会 ...
- Leetcode之回溯法专题-78. 子集(Subsets)
Leetcode之回溯法专题-78. 子集(Subsets) 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = ...
- [Python] socket实现TFTP上传和下载
一.说明 本文主要基于socket实现TFTP文件上传与下载. 测试环境:Win10/Python3.5/tftpd64. tftpd下载:根据自己的环境选择下载,地址 :http://tftpd32 ...
- PHP识别简单的图片上面的数字(可扩展)
1.场景 最近在学习图片处理,就是特意把数字生成一个图片,然后再用程序去识别图片的数字.这就有了一下的学习过程. 2.原理分析 2.1 首先是将图片像素化,二值化,然后和字模去对比(需要相对于配置字模 ...
- ubuntu 用户无法/循环登陆的问题
安装oracle数据库的时候需要新建用户oracle,使用的命令为:useradd oracle.这样做直接创建了用户oracle,但并没有在home目录下创建oracle用户的文件.在log out ...