线程池中线程的停止及ExecutorService 中 shutdown()、shutdownNow()、awaitTermination() 含义和区别

文章目录

  • 线程池中线程的停止及ExecutorService 中 shutdown()、shutdownNow()、awaitTermination() 含义和区别
    • 1.线程池停止的常见方法
      • shutdown():
      • shutdownNow():
      • awaitTermination() 方法
      • shutdown() 和 shutdownNow() 的区别
      • shutdown() 和 awaitTermination() 的区别
    • 2.线程池中线程的停止机制
      • 线程的中断
      • 任务的响应式停止
    • 3.线程池停止时的常见注意事项
    • 4.总结

线程池中线程的停止及ExecutorService 中 shutdown()、shutdownNow()、awaitTermination() 含义和区别

线程池提供了一种高效的线程复用机制,线程池中的线程执行完任务后,并不会立即被销毁,而是会继续保持活动状态,等待下一个任务的到来。

在 Java 中,线程池管理着多个线程的创建、执行和销毁。线程池中的线程通常在完成任务后等待新的任务或被销毁**。线程池本身并不直接停止线程**,而是通过任务的完成和线程池的关闭来管理线程生命周期。

在 Java 中,线程池本身不会自动停止,必须通过调用线程池的关闭方法来停止线程池中的线程。常见的线程池停止方法有:

1.线程池停止的常见方法

线程池的停止主要通过 ExecutorService 接口提供的方法来控制:

使用 ExecutorService 类时,经常用到 shutdown()shutdownNow()awaitTermination() 3个方法.

shutdown():

shutdown():停止接收新任务,原来的任务继续执行

shutdown() 方法用于平滑地关闭线程池。调用此方法后,线程池会停止接受新任务,但会继续执行当前队列中等待的任务,直到所有任务执行完毕。

特点

  • 调用 shutdown() 后,线程池不再接受新的任务。
  • 线程池中的线程会继续执行已提交的任务。(已经提交的任务(包括正在跑的和队列中等待的),会继续执行完成;)
  • 线程池中的线程不会被立即终止,直到所有任务执行完成。
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(() -> {
    // 执行任务
});

// 请求关闭线程池,不会立即终止线程池中的线程
executorService.shutdown();  

shutdownNow():

shutdownNow():停止接收新任务,原来的任务停止执行

shutdownNow() 方法用于强制关闭线程池。它会尝试停止所有正在执行的任务,并返回尚未执行的任务列表。这个方法会尝试中断线程池中的所有线程,并停止执行新的任务。已提交的任务不会再被接收.

特点

  • 跟 shutdown() 一样,先停止接收新submit的任务;

  • 调用 shutdownNow() 后,线程池会尝试停止正在执行的任务,并清空任务队列。

  • 如果线程正在执行任务,它们会被中断。

    (注意:它并不会强制中止正在运行的任务,而是试图通过中断来停止任务。)

    尝试将正在执行的任务interrupt中断;

  • 任务队列中的任务会被返回,可以手动决定是否继续执行这些任务。

    返回未执行的任务列表;

说明:它试图终止线程的方法是通过调用 Thread.interrupt() 方法来实现的,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt() 方法是无法中断当前的线程的。所以,shutdownNow() 并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。

ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(() -> {
    // 执行任务
});

List<Runnable> remainingTasks = executorService.shutdownNow();  // 请求强制关闭线程池,返回未执行的任务列

awaitTermination() 方法

awaitTermination() 方法不是用于停止线程池的,而是用于等待线程池中的任务完成。在调用 shutdown()shutdownNow() 后,可以调用此方法来阻塞当前线程,直到线程池中的任务完成或超时。

当前线程阻塞,直到:

  • 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
  • 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
  • 或者 线程被中断,抛出InterruptedException

然后会监测 ExecutorService 是否已经关闭,返回true(shutdown请求后所有任务执行完毕)或false(已超时)

特点

  • 它会阻塞当前线程,直到线程池中的所有任务完成,或者超时。
  • 需要配合 shutdown()shutdownNow() 使用。
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(() -> {
    // 执行任务
});

executorService.shutdown();  // 请求关闭线程池
try {
    if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
        executorService.shutdownNow();  // 如果超时,则强制关闭线程池
    }
} catch (InterruptedException e) {
    executorService.shutdownNow();
}

shutdown() 和 shutdownNow() 的区别

shutdown() 只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。

shutdownNow() 能立即停止线程池,正在跑的和正在等待的任务都停下了。

shutdown() 和 awaitTermination() 的区别

shutdown() 后,不能再提交新的任务进去;但是 awaitTermination() 后,可以继续提交。

awaitTermination()是阻塞的,返回结果是线程池是否已停止(true/false);shutdown() 不阻塞。

2.线程池中线程的停止机制

线程的中断

线程池中的线程停止时,通常是通过 中断 线程来实现的。线程池中的线程在执行任务时会被要求定期检查是否被中断,若检测到中断信号(例如通过 Thread.interrupted()isInterrupted()),线程会终止正在执行的任务并退出。

  • **Thread.interrupt()**:该方法会中断线程,并设置线程的中断标志。
  • **Thread.isInterrupted()**:判断线程是否被中断。
  • **Thread.interrupted()**:清除当前线程的中断状态并返回中断状态。
Runnable task = () -> {
    try {
        while (!Thread.currentThread().isInterrupted()) {
            // 执行任务
            // 如果需要退出,可以抛出 InterruptedException 或检查中断状态
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();  // 保持中断状态
    }
};

ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(task);

// 关闭线程池时中断线程
executorService.shutdownNow();  // 会尝试中断线程池中的所有线程

任务的响应式停止

如果任务本身执行较为复杂且需要响应停止命令,可以在任务中添加适当的逻辑来响应线程池的停止请求。例如,在任务执行时,可以定期检查 Thread.currentThread().isInterrupted() 或者通过捕获 InterruptedException 来判断线程是否需要停止。

  • 在执行任务时,可以通过抛出 InterruptedException 来停止正在执行的任务。
Runnable task = () -> {
    try {
        for (int i = 0; i < 100; i++) {
            if (Thread.currentThread().isInterrupted()) {
                break;  // 响应中断,退出任务
            }
            // 执行任务
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();  // 保持中断状态
    }
};

ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(task);

// 停止线程池
executorService.shutdownNow();  // 会中断正在执行的线程

3.线程池停止时的常见注意事项

  • 线程池不会立即停止:调用 shutdown()shutdownNow() 后,线程池会正常终止执行当前的任务,但是它们并不会立即停止线程池中的所有线程。如果任务尚未完成,线程池会继续处理剩余的任务,直到所有任务执行完毕或被中断。
  • 中断并非立即停止:当调用 shutdownNow() 请求线程池停止时,线程池会尝试中断所有线程,但并不保证线程立即停止。线程的停止依赖于任务本身是否能够响应中断,只有在任务响应中断时,线程才会结束执行。
  • 避免死锁:当关闭线程池时,如果线程池中的线程依赖于其他线程的结果(例如,多个线程之间存在等待依赖关系),这可能会导致死锁。在设计线程池任务时,要避免这种情况。
  • 线程池关闭后无法提交新任务:一旦调用了 shutdown()shutdownNow(),线程池会停止接受新任务。如果想要提交新任务,必须创建新的线程池实例。

4.总结

在 Java 中,线程池的停止可以通过 shutdown()shutdownNow() 方法来实现:

  • **shutdown()**:平滑关闭线程池,不会立即中止任务,会等待现有任务执行完毕。
  • **shutdownNow()**:强制关闭线程池,会尝试中断正在执行的任务,并清空任务队列。
  • **awaitTermination()**:等待线程池中的所有任务完成,或者超时。

1、优雅的关闭,用 shutdown()

2、想立马关闭,并得到未执行任务列表,用shutdownNow()

3、优雅的关闭,并允许关闭声明后新任务能提交,用 awaitTermination()

4、关闭功能 【从强到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()

线程池中的线程停止依赖于任务的中断机制,线程池会通过中断线程来请求停止正在执行的任务,因此在任务执行过程中,需要处理好中断逻辑,确保线程能够响应停止请求。

在 Java 中,最初设计了 Thread.stop() 方法来强制停止线程,但该方法已被废弃,因为它会导致线程在中途停止时保持不一致的状态,容易引发资源泄露或不完整的操作。

aitTermination()

线程池中的线程停止依赖于任务的中断机制,线程池会通过中断线程来请求停止正在执行的任务,因此在任务执行过程中,需要处理好中断逻辑,确保线程能够响应停止请求。

在 Java 中,最初设计了 Thread.stop() 方法来强制停止线程,但该方法已被废弃,因为它会导致线程在中途停止时保持不一致的状态,容易引发资源泄露或不完整的操作。

你可能感兴趣的:(java,后端,面试)