Java终止线程的三种方式

中止一个线程一般意味着在线程处理任务完成以前停掉正在作的操做,也就是放弃当前的操做。html

在 Java 中有如下 3 种方法能够终止正在运行的线程:java

  1. 使用退出标志,使线程正常退出,也就是当 run() 方法完成后线程停止。
  2. 使用 stop() 方法强行终止线程,可是不推荐使用这个方法,该方法已被弃用。
  3. 使用 interrupt 方法中断线程。

1. 使用标志位终止线程

在 run() 方法执行完毕后,该线程就终止了。可是在某些特殊的状况下,run() 方法会被一直执行;好比在服务端程序中可能会使用 while(true) { ... } 这样的循环结构来不断的接收来自客户端的请求。此时就能够用修改标志位的方式来结束 run() 方法。数据库

public class ServerThread extends Thread {
    //volatile修饰符用来保证其它线程读取的老是该变量的最新的值
    public volatile boolean exit = false; 

    @Override
    public void run() {
        ServerSocket serverSocket = new ServerSocket(8080);
        while(!exit){
            serverSocket.accept(); //阻塞等待客户端消息
            ...
        }
    }
    
    public static void main(String[] args) {
        ServerThread t = new ServerThread();
        t.start();
        ...
        t.exit = true; //修改标志位,退出线程
    }
}

2. 使用 stop() 终止线程

经过查看 JDK 的 API,咱们会看到 java.lang.Thread 类型提供了一系列的方法如 start()、stop()、resume()、suspend()、destory()等方法来管理线程。可是除了 start() 以外,其它几个方法都被声名为已过期(deprecated)。安全

虽然 stop() 方法确实能够中止一个正在运行的线程,可是这个方法是不安全的,并且该方法已被弃用,最好不要使用它。
JDK 文档中还引入用一篇文章来解释了弃用这些方法的缘由:《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?》oracle

为何弃用stop:ide

  1. 调用 stop() 方法会马上中止 run() 方法中剩余的所有工做,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(一般状况下此异常不须要显示的捕获),所以可能会致使一些清理性的工做的得不到完成,如文件,数据库等的关闭。
  2. 调用 stop() 方法会当即释放该线程所持有的全部的锁,致使数据得不到同步,出现数据不一致的问题。

例如,存在一个对象 u 持有 ID 和 NAME 两个字段,假如写入线程在写对象的过程当中,只完成了对 ID 的赋值,但没来得及为 NAME 赋值,就被 stop() 致使锁被释放,那么当读取线程获得锁以后再去读取对象 u 的 ID 和 Name 时,就会出现数据不一致的问题,以下图:ui

3. 使用 interrupt() 中断线程

如今咱们知道了使用 stop() 方式中止线程是很是不安全的方式,那么咱们应该使用什么方法来中止线程呢?答案就是使用 interrupt() 方法来中断线程线程

须要明确的一点的是:interrupt() 方法并不像在 for 循环语句中使用 break 语句那样干脆,立刻就中止循环。调用 interrupt() 方法仅仅是在当前线程中打一个中止的标记,并非真的中止线程。code

也就是说,线程中断并不会当即终止线程,而是通知目标线程,有人但愿你终止。至于目标线程收到通知后会如何处理,则彻底由目标线程自行决定。这一点很重要,若是中断后,线程当即无条件退出,那么咱们又会遇到 stop() 方法的老问题。server

事实上,若是一个线程不能被 interrupt,那么 stop 方法也不会起做用。

咱们来看一个使用 interrupt() 的例子:

public class InterruptThread1 extends Thread{

    public static void main(String[] args) {
        try {
            InterruptThread1 t = new InterruptThread1();
            t.start();
            Thread.sleep(200);
            t.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        super.run();
        for(int i = 0; i <= 200000; i++) {
            System.out.println("i=" + i);
        }
    }
    
}

输出:

从输出的结果咱们会发现 interrupt 方法并无中止线程 t 中的处理逻辑,也就是说即便 t 线程被设置为了中断状态,可是这个中断并不会起做用,那么该如何中止线程呢?

这就须要使用到另外两个与线程中断有关的方法了:

public boolean Thread.isInterrupted() //判断是否被中断
public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态

这两个方法使得当前线程可以感知到是否被中断了(经过检查标志位)。

因此若是但愿线程 t 在中断后中止,就必须先判断是否被中断,并为它增长相应的中断处理代码:

@Override
public void run() {
    super.run();
    for(int i = 0; i <= 200000; i++) {
        //判断是否被中断
        if(Thread.currentThread().isInterrupted()){
            //处理中断逻辑
            break;
        }
        System.out.println("i=" + i);
    }
}

输出结果,for 循环在执行完成前就提早结束了:

在上面这段代码中,咱们增长了 Thread.isInterrupted() 来判断当前线程是否被中断了,若是是,则退出 for 循环,结束线程。

这种方式看起来与以前介绍的“使用标志位终止线程”很是相似,可是在遇到 sleep() 或者 wait() 这样的操做,咱们只能经过中断来处理了

public static native void sleep(long millis) throws InterruptedException

Thread.sleep() 方法会抛出一个 InterruptedException 异常,当线程被 sleep() 休眠时,若是被中断,这会就抛出这个异常。 (注意:Thread.sleep() 方法因为中断而抛出的异常,是会清除中断标记的。)

相关文章
相关标签/搜索