1 前言
前面的文章线程以及线程的常用方法介绍了线程的使用。线程除了在执行处理完成其任务后会停止外,还可以通过一些方法进行干预停止其运行。停止一个线程意味着在线程处理完成任务之前结束其正在执行的操作。
在java中可以使用以下三种终止线程的方法。
2 正文
1、使用退出标志
使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。比如:
package com.jiangxia.chap1; /** * 线程停止的方法之:使用退出标志 * author:jiangxia * date:2021-04-15 */ public class Demo12 { public static void main(String[] args) throws InterruptedException { Demo12Thread t = new Demo12Thread(); t.start(); Thread.sleep(2000); t.stopThread(); } } class Demo12Thread extends Thread{ private boolean flag = true; @Override public void run() { try { while (flag){ System.out.println("Time="+System.currentTimeMillis()); Thread.sleep(1000); } System.out.println("线程执行结束"); }catch (InterruptedException e){ e.printStackTrace(); } } /** * 定义一个停止线程的方法 */ public void stopThread(){ //将标志位改为flase flag = false; } } 复制代码
2、stop方法强制结束线程
java种提供了Thread.stop()方法停止一个线程,虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被废弃的方法。调用stop()方法时会抛出java.lang.ThreadDeath异常。官方文档对其的解释如下:
该方法天生是不安全的。使用thread.stop()停止一个线程,导致释放(解锁)所有该线程已经锁定的监视器(因沿堆栈向上传播的未检查异常ThreadDeath而解锁)。如果之前受这些监视器保护的任何对象处于不一致状态,则不一致状态的对象(受损对象)将对其他线程可见,这可能导致任意的行为。
package com.jiangxia.chap1; /** * 停止线程之stop方法 * author:jiangxia * date:2021-04-15 */ public class Demo13 { public static void main(String[] args) throws InterruptedException { Thread t = new Demo13Thread(); t.start(); Thread.sleep(2000); //该方法有个删除线表示已经被删除 不建议使用 //stop方法会产生异常 t.stop(); } } class Demo13Thread extends Thread{ @Override public void run() { try{ while(true){ System.out.println("run方法 Time="+System.currentTimeMillis()); Thread.sleep(1000); } }catch (InterruptedException e){ e.printStackTrace(); }catch (ThreadDeath threadDeath){ System.out.println("进入catch块"); threadDeath.printStackTrace(); } } } 复制代码
通过idea可以发现stop方法有个删除线,说明stop方法已经被作废,如果强制让线程停止有可能使一些清理性的工作得不到完成。另外一个原因是对锁定的对象进行『解锁』,导致数据得不同同步的处理,出现数据不一致性的问题。所以使用stop()方法停止线程则是非常暴力的,由于stop()方法以及在JDK中被标明为“过期/作废”的方法,所以它在功能上具有缺陷,所以不建议在程序张使用stop()方法。比如:
package com.jiangxia.chap1; public class Demo14 { public static void main(String[] args) throws InterruptedException { Demo14Info info = new Demo14Info(); Thread t = new Demo14Thread(info); t.start(); Thread.sleep(200); t.stop(); //因为stop方法结束了线程,所以只更新了username的值,而password的值还是之前的值没有被更新 System.out.println("username="+info.getUsername()+":password="+info.getPassword()); } } class Demo14Thread extends Thread{ private Demo14Info info; public Demo14Thread(Demo14Info info){ this.info = info; } @Override public void run() { info.updateInfo("张飞","1111111"); } } class Demo14Info{ private String username="刘备"; private String password="222222"; public String getUsername() { return username; } public String getPassword() { return password; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } synchronized public void updateInfo(String username,String password){ try { this.username = username; Thread.sleep(1000); this.password = password; } catch (InterruptedException e) { e.printStackTrace(); } } } 复制代码
因为stop方法结束了线程,所以上述代码只更新了username的值,而password的值还是之前的值没有被更新。
3、使用interrupt方法中断线程
interrupt()其作用是中断此线程,注意此线程不一定是当前线程,而是值调用该方法的Thread实例所代表的线程,调用interrupt方法不会真正的结束线程,在当前线程中打上一个停止的标记,线程仍然会继续运行。
Thread类提供了interrupted方法测试当前线程是否中断,即检查中断的标志,返回一个boolean值并清除中断的状态,第二次再调用时中断状态已经被清楚,所以会返回false。
isInterrupted方法测试线程是否已经中断,不清除中断状态。
对这三个方法的总结可以归纳如下:
public void Thread.interrupt() // 无返回值 public boolean Thread.isInterrupted() // 有返回值 public static boolean Thread.interrupted() // 静态,有返回值 复制代码
比如:
package com.jiangxia.chap1; /** * 停止线程之interrupt方法 * author:jiangxia * date:2021-04-15 */ public class Demo15 { public static void main(String[] args) { Thread thread = new Thread15(); thread.start(); //使用interrupt方法中断线程 thread.interrupt(); } } class Thread15 extends Thread{ @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("run方法:i="+i); } } } 复制代码
调用interrupt方法不会真正的结束线程,而是在当前线程上打上一个停止的标记。
package com.jiangxia.chap1; /** * 停止线程之isInterrupted方法 * author:jiangxia * date:2021-04-15 */ public class Demo15 { public static void main(String[] args) { Thread thread = new Thread15(); thread.start(); //使用interrupt方法中断线程 thread.interrupt(); System.out.println("是否已经停止1:"+thread.isInterrupted()); System.out.println("是否已经停止2:"+Thread.interrupted()); } } class Thread15 extends Thread{ @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("run方法:i="+i); } } } 复制代码
thread.isInterrupted方法是检查Thread15是否被打上停止的标记。
Thread.interrupted方法是检查主线程是否打上停止的标记。
interrupted()与isInterrupted()的唯一区别是,前者会读取并清除中断状态,后者仅读取状态。
测试当前线程是否已经中断,线程的中断的状态由方法清除,如果两次连续调用该方法,第二次调用将返回false。
package com.jiangxia.chap1; public class Demo17 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thred17(); //启动线程 thread.start(); //Thread.sleep(1000); thread.interrupt(); } } class Thred17 extends Thread{ @Override public void run() { for (int i = 0; i <1000 ; i++) { if(this.isInterrupted()){ System.out.println("已经是停止状态!"); break; } System.out.println("i="+i); } System.out.println("这里是结束循环后的代码"); } } 复制代码
如果线程中有sleep代码,不管是否进入到sleep的状态,如果调用了interrupt方法,都会产生异常信息。比如:
package com.jiangxia.chap1; public class Demo17 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thred17(); //启动线程 thread.start(); //Thread.sleep(1000); thread.interrupt(); } } class Thred17 extends Thread{ @Override public void run() { try{ for (int i = 0; i <1000 ; i++) { if(this.isInterrupted()){ System.out.println("已经是停止状态!"); //如果使用break那么会导致其他线程不知道,所以在这里直接抛出异常 // break; throw new InterruptedException(); } System.out.println("i="+i); } System.out.println("这里是结束循环后的代码"); }catch (InterruptedException e){ e.printStackTrace(); } } } 复制代码
package com.jiangxia.chap1; public class Demo18 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread18(); t.start(); Thread.sleep(20); t.interrupt(); } } class Thread18 extends Thread{ @Override public void run() { try { for (int i = 0; i < Integer.MAX_VALUE; i++) { String s = new String(); } System.out.println("开始线程"); Thread.sleep(20000); System.out.println("结束线程"); }catch (InterruptedException e){ System.out.println("异常进入catch代码块"); e.printStackTrace(); } } } 复制代码
3 总结
以上就是关于Java种如何停止一个线程的三种方法。在Java中没有提供任何机制来安全地终止线程。stop方法因为不安全已经被废弃了。但java提供了中断机制(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的的工作。除此在外可以通过设置退出标志的方式控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。