圆咕噜咕噜 发表于 2024-5-14 01:46:36

阿里一面:Java中怎样停止线程?

引言

在Java多线程编程中,精确且安全地停止线程是一项关键技能。简朴粗暴地“杀死”线程不仅可能导致数据不一致性,还可能引发各种难以猜测的错误。本文将探讨几种在Java中优雅地停止线程的方法,以确保程序的健壮性和可靠性。
使用标记位(共享变量)停止线程

一种常见的做法是使用一个boolean类型的标记位来控制线程的执行。线程在执行任务的过程中不停检查标记位的状态,当标记位被设置为true时,线程停止执行任务,从而退出线程。
class StoppableThread extends Thread {
    private volatile boolean isStopped = true;

    @Override
    public void run() {
      while (isStopped) {
            // 线程执行任务的代码
      }
      // 清理资源并在检查到退出标志时退出
    }

    public void stopThread() {
      // 在需要时设置标识=false,停止线程   
isStopped = false;
    }
}这种方式简朴易用,它可以控制线程的停止机遇,机动性较高,实用于简朴的线程任务。但是如果任务执行时间过长大概任务中有阻塞操作,可能无法实时响应停止请求。而且这种方式的任务中需要周期性地检查标记位状态,可能会影响性能。
使用Thread.stop()方法(已过时)

虽然Thread类提供了stop()方法用于停止线程,但是这个方法已经被标记为过时(deprecated),不推荐使用。因为它可能会导致线程不安全的终止,引发一系列题目,如不释放锁、资源泄漏等。
https://coderacademy.oss-cn-zhangjiakou.aliyuncs.com/blogcontent/20240301010837.png
stop()方法确实可以停止线程的。
使用interrupt()方法

Java提供了一种基于停止模型的方式来请求线程停止。停止并不是立即停止线程的运行,而是设置一个停止标记,线程需要自行检查并响应这个停止信号。
public static void main(String[] args) {
    // 默认情况下,interrupted标记位为false。该方法用于检查当前线程的中断状态,返回true表示线程已被中断,返回false表示线程未被中断
    System.out.println("初始状态下的Interrupted标记位:" + Thread.currentThread().isInterrupted());
    // 执行Interrupted。该方法用于中断当前线程,它会设置当前线程的中断标记位为true。
    Thread.currentThread().interrupt();
    System.out.println("执行Interrupted后的Interrupted标记位:" + Thread.currentThread().isInterrupted());
    // 该方法用于检查当前线程的中断状态,并清除中断状态。如果当前线程被中断,则返回true,并清除中断状态;如果当前线程未被中断,则返回false。
    System.out.println("返回当前线程:" + Thread.interrupted());
    System.out.println(Thread.interrupted());
}初始状态下的Interrupted标记位:false
执行Interrupted后的Interrupted标记位:true
返回当前线程:true
false在这段代码中,两次调用interrupted()方法,第一次返回true(因为之前调用了interrupt()方法),第二次返回false(因为在第一次调用后,停止状态被扫除)。
Thread myThread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
      // 执行任务...
    }
    // 清理资源并在检查到中断标志时退出
});

myThread.start();
// 在需要停止线程时
myThread.interrupt();对于阻塞操作(如IO操作或wait()方法),当线程在阻塞状态时收到停止请求,某些方法会抛出InterruptedException。在这种情况下,处理方式通常是捕获异常,执行必要的清算操作,并根据需要决定是否退出线程。
try {
    synchronized (someLock) {
      someLock.wait();// 在wait期间,如果线程被中断,会抛出InterruptedException
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // 重新设置中断标志,以便后续代码可以感知
    // 清理并退出线程
}这种方式可以优雅地停止线程,即使线程处于阻塞状态也能够实时响应停止请求。通过捕获InterruptedException异常,可以在停止线程时举行清算工作。但是需要在线程任务中处理InterruptedException异常,这无形之中增加了代码复杂度。而且如果任务不是阻塞的,需要在任务中周期性地检查线程的停止状态。
使用线程池来管理线程

使用ExecutorService来管理线程可以更加机动地控制线程的生命周期。通过调用ExecutorService的shutdown()或shutdownNow()方法来停止线程池中的全部线程,从而优雅地停止线程。
ExecutorService executor = Executors.newFixedThreadPool(5);

// 提交任务到线程池
executor.submit(new MyTask());

// 关闭线程池
executor.shutdown();通过ExecutorService可以更加机动地管理线程,包括启动、执行和停止。但是他是停止线程池中的全部线程。在Java中,没有直接提供停止线程池中特定线程的方法。因为线程池是一种管理线程的容器,它负责管理线程的创建、调度和销毁。但是我们可以使用Future对象来表示线程池中的任务,通过调用Future的cancel(boolean mayInterruptIfRunning)方法,可以尝试取消任务的执行。在调用cancel(true)时,表示可以停止正在运行的任务。
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(() -> {
    // 执行任务...
    // 在适当位置检查Thread.currentThread().isInterrupted()
});

// 在需要停止任务时
future.cancel(true);// 参数为true表示可以中断正在运行的任务
executor.shutdown();这种方式虽然调用了cancel(true),任务也需要在恰当的机遇检查停止状态并做出相应处理。如果任务已经被停止,可能无法继续执行,需要在任务中捕获InterruptedException异常并举行恰当的清算工作。
总结

优雅地停止线程是编写高质量Java多线程程序的关键之一。在选择停止线程的方法时,需要考虑线程的执行情况、任务特性以及程序设计的要求。一般而言,使用interrupt()方法大概ExecutorService来管理线程是较为推荐的做法,可以有用地避免线程不安全的终止以及资源泄漏等题目。
本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、口试题、程序员攻略等

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 阿里一面:Java中怎样停止线程?