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
状态下的线程对于中断操作是敏感的,他们会抛出异常并清空中断标志位。
有兴趣的话可以关注我的公众号或者头条号,说不定会有意外的惊喜。
原文地址:https://www.cnblogs.com/death00/p/11585028.html
- Dance In Heap(四):一些堆利用的方法(下)
- Mac系统的Proton恶意软件:卷!土!重!来!
- Erlang语言学习入门
- Android使用Ant进行apk多渠道打包
- Dance In Heap(三):一些堆利用的方法(中)
- Android性能优化篇:使用软引用和弱引用
- 使用泛型委托,构筑最快的通用属性访问器
- 记录容易忘记的方法
- 无需重新编译代码,在线修改表单
- 使用反射+缓存+委托,实现一个不同对象之间同名同类型属性值的快速拷贝
- 结构变量作为方法的参数调用,在方法内部使用的“坑”你遇到过吗?
- 使用表达式树,让访问者直接执行“角色”对象的方法
- 【更正】“给自定义控件(Web Control)添加事件的几种方法”有一个不太准确的地方。
- 在数据库上实现类似铁路售票锁票功能
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP关键字this指向当前对象指针
- 一起来学matlab-matlab学习笔记8 基本绘图命令_4 LineSpec线条设定
- PHP中的static静态变量的使用方法详解
- 一起来学matlab-matlab学习笔记8 基本绘图命令_6 三维绘图
- 一起来学matlab-matlab学习笔记8 基本绘图命令_5 初级二维绘图/交互式绘图
- PHP实现页面跳转的几种方法
- 一起来学matlab-matlab学习笔记9 高级绘图命令_2 图形的高级控制_视点控制和图形旋转_色图和颜色映像_光照和着色
- 一起来学matlab-matlab学习笔记9 高级绘图命令_1 图形对象_根对象,轴对象,用户控制对象,用户菜单对象
- PHP 如何阻止用户上传成人照片或者裸照
- 一起来学matlab-字符串操作 10_4 MATLAB中的字符串表示
- 一起来学演化计算-matlab优化函数fminunc
- 一起来学matlab-matlab学习笔记10 10_1一般运算符
- 每日手撕一道算法题-20. 有效的括号
- 一起来学matlab-数组取值 MATLAB中的字符串符号
- 递归解决遍历问题