Java:一个停止线程的方法——interrupt
一、前言
之前本人写了一篇防止Controller中的线程被重复调用的文章,大概代码如下:
//sonarqube检查要求static变量必须是final,为避开检查,使用final HashMap
public final static HashMap<String,Thread> threadMap = new HashMap<>();
--------------------------------------------------
Thread nowThread = threadMap.get("nowThread");
if(nowThread != null && nowThread.isAlive()){
LOG.info("当前存在正在执行的线程,本次线程不执行,请稍后再试");
return;
}
else{
threadMap.put("nowThread",Thread.currentThread());
}
//主要代码省略
......
//线程执行完毕,置空
threadMap.put("nowThread",null);
后来,由于担心这个线程会卡死,导致后续正常调用该线程的操作无法进行,因此加了个手动停止线程运行的方法(interrupt):
//传入一个type参数,如果为3,中断上一个线程的执行
if(type == 3){
try{
Thread nowThread = threadMap.get("nowThread");
//中断上一个线程
nowThread.interrupt();
threadMap.put("nowThread",null);
String backMsg = "线程interrupt成功!";
LOG.info(backMsg);
return;
}catch(Exception e){
threadMap.put("nowThread",null);
String backMsg = "线程interrupt失败,只将map置空!";
LOG.error(backMsg,e);
return;
}
}
然而,仅仅这样并不能停止线程运行。
二、停止线程运行的方法
-
使用标志位停止线程,还行,但是遇到sleep时的线程就无法停止了,必须等线程sleep结束、运行到判断标志位步骤时才行。
-
使用stop()停止线程,不安全,已不推荐使用。会立即停止线程运行,会使一些清理性的工作的得不到完成,如文件,数据库等的关闭;会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。
-
使用interrupt()中断线程,需要配合代码逻辑实现停止线程。
需要注意,调用 interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程;如果直接将线程停止,就会和stop一样出现问题。
因此还需要配合代码逻辑实现。
三、正确使用interrupt停止线程运行
先看下例子:
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(){
@Override
public void run(){
//一个循环线程
while(true){
//一种结束情况
if(this.currentThread().isInterrupted()){
System.out.println("线程被打断,停止运行");
break;
}else{
System.out.println("线程在运行");
try{
//Thread.sleep(0L);
Thread.sleep(1000L);
}catch(InterruptedException e){
//另一种结束情况
System.out.println("线程睡眠时被打断,停止运行");
break;
}
}
}
}
};
t1.start();
Thread.sleep(5000L);
t1.interrupt();
System.out.println("主线程打断t1完成");
}
上方的代码中,先启动一个线程,然后主线程在5秒后打断它,它有两种停止情况:
-
执行if判断时,发现标志位为true,因此执行break,之后停止运行。
-
线程还在sleep,此时发现被打断,会抛出InterruptedException,然后被catch到,执行break,之后停止运行。
结合实际代码,如果线程中不需要sleep,则判断isInterrupted()即可;
如果线程中存在sleep,则catch中使用break、return之类的停止线程即可;
当然也可以两种都写。
本人的代码中有sleep,因此只在catch中加了break,没有判断标志位。
四、总结
-
使用interrupt停止线程比标志位停止线程的好处在于,它不仅能通过标志判断是否要停止线程,而且当线程处于sleep状态时,使用interrupt就可以停止线程,而标志位不行。
-
使用interrupt停止线程比stop停止线程的好处在于,stop不安全,会产生难以预料的后果,而interrupt不会。
-
停止线程时,不仅要直接调用interrupt(),还要写好相应的代码逻辑,一种逻辑与标志位停止线程类似,另一种逻辑要注意在try中写Thread.sleep(当你的线程需要sleep时),在catch中写break、return之类的方法。
PS:之前写sleep时,都是这么写的:
try{
Thread.sleep(5000L);
}catch(Exception e){}
现在终于知道应该怎么写了……