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

在 Java 中有以下 3 种方法可以终止正在运行的线程:

  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() 方法确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且该方法已被弃用,最好不要使用它。

为什么弃用stop:

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

3. 使用 interrupt() 中断线程

现在我们知道了使用 stop() 方式停止线程是非常不安全的方式,那么我们应该使用什么方法来停止线程呢?答案就是使用 interrupt() 方法来中断线程

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

 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);
    }
}