Java中终止线程的三种方法

时间:2021-12-18 20:26:14

终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法。即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行。比如设置一个标志来控制循环是否执行,通过这种方式让线程离开run()方法。

第一种 使用Thread类提供的stop()方法或suspend()方法强制终止线程(不安全,不要用)

第二种 使用volatile标记位退出线程

第三种 使用 interrupt()方法终止线程

由于其他原因导致线程停滞(如 I/O),进入非运行状态,停止线程的基本思路也是触发一个异常,而这个异常与导致线程停滞的原因相关。


第一种 使用Tread类提供的stop()方法或suspend()方法强制终止线程(不安全,不要用)

在当前线程中使用Thread类提供的stop()方法来终止其它线程,它会释放已经锁定的所有监视资源。如果当前任何一个受到这些监视资源保护的对象处于一个不一致的状态,其他的线程将会看到这个不一致的状态,这可能会导致程序执行的不确定性,且这种问题很难被定位。

在当前线程中使用Thread类提供的suspend()方法来挂起其它线程,然后使用resume()方法恢复。该方法容易引起死锁。线程在调用suspend()方法时不会释放锁,这就会导致一个问题:如果在当前线程中使用一个suspend()挂起一个持有锁的线程,如果当前线程也试图取得同样的对象锁,程序就会发生死锁。

鉴于以上两种方法的不安全性,Java语言已经不建议使用以上两种方法来终止线程。

第二种 使用volatile标记位退出线程

就是在现线程定义中设置一个boolean型的标记位,在线程的run()方法中根据这个标记位是true还是false来判断是否退出。这种情况一般是将任务放在run()方法中的一个while循环中执行。

 package com.test01.stopThread;

 class MyThread extends Thread {
private volatile boolean exit = false; // volatile保证exit的同步 public void exit(){
exit = true; // 标记位
} @Override
public void run() {
while (!exit){ // 通过标记位控制while循环中的任务
System.out.println("This is a thread.");
}
}
} public class ThreadFlag {
public static void main(String [] args) throws Exception{
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start(); // 启动线程thread1
thread2.start(); // 启动线程thread2
Thread.sleep(1000); // 当前线程睡眠5秒钟
thread1.exit(); // 终止thread1
thread2.exit(); // 终止thread2
thread1.join(); // 当前线程等待thread1执行完
thread2.join(); // 当前线程等待thread2执行完
System.out.println(" Thread had been exited");
}
}

第三种 使用 interrupt ()方法终止线程

使用interrupt()方法来终止来终止线程分为两种情况:

1)线程处于阻塞状态,如使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态,从而让我们有机会结束这个线程的执行。通常很多人认为只要调用interrupt方法线程就会结束,实际上是错的, 一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正常结束run方法。

2)线程未处于阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。

为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑。

阻塞时抛出异常的情景

  package com.test01.stopThread;

  class MyThread01 extends Thread{
@Override
public void run(){
try {
sleep(50000); // sleep()是Thread类的静态方法
System.out.println("线程正在执行。");
} catch (InterruptedException e){ // 阻塞时sleep()方法会抛出异常
System.out.println("线程终止。");
System.out.println(e.getMessage());
break; // 捕获异常之后,退出循环
}
}
} public class Interrupt1 {
public static void main(String [] args)throws Exception {
MyThread01 thread01 = new MyThread01();
thread01.start();
System.out.println("在50秒内按任意键终止线程");
System.in.read();
thread01.interrupt(); // 中断线程thread01
thread01.join(); // 当前线程等待thread01执行完
System.out.println("线程已经结束");
}
}

非阻塞不抛出异常的情景

 package com.test01.stopThread;

 class MyThread01 extends Thread{
@Override
public void run(){
while(!isInterrupted()){ // 可以替换成Thread.interrupted()
System.out.println("线程执行中");
}
}
} public class Interrupt1 {
public static void main(String [] args)throws Exception {
MyThread01 thread01 = new MyThread01();
thread01.start();
Thread.sleep(10);
thread01.interrupt(); // 中断线程thread01
thread01.join(); // 当前线程等待thread01执行完
System.out.println("线程已经结束");
}
}

注意:在Thread类中有两个方法可以判断线程是否被中断。

一个是Thread类的静态方法interrupted(),用来判断当前线程是否被中断。

一个是非静态方法isInterrupted(),判断调用这个方法的线程是否被中断,即可以在当前线程中判断其他线程是否被中断。