常见面试题-系统停止时如何处理正在执行和等待执行的多线程任务

话不多说,直接上方案

第一步:通过ExecutorService的shutdown、awaitTermination以及shutdownNow实现线程的关闭和执行等待
第二步:通过Runtime.getRuntime().addShutdownHook(thread)声明虚拟机关机挂钩

1.ExecutorService线程操作

1.1.shutdownNow

是 Java 中 ExecutorService 接口的一个方法,用于尝试立即关闭线程池。当调用这个方法时,它会尝试执行以下操作:

  1. 停止所有正在执行的任务。
  2. 停止所有闲置(未被执行)的任务。
  3. 返回那些未得到执行的任务列表。

具体来说,shutdownNow() 会尝试停止所有正在执行的任务,并且不会启动队列中等待的任务。如果任务正在执行,那么这些任务可能会通过 Thread.interrupt() 方法来中断它们,但是任务是否能够响应中断完全取决于任务本身的实现。如果任务没有正确处理中断请求(即没有检查中断状态或者没有正确处理 InterruptedException),那么即使调用了 shutdownNow(),这些任务也可能会继续执行。

需要注意的是,shutdownNow() 不保证能立即停止所有正在执行的任务,因为这依赖于任务的响应情况和实现。此外,它返回的列表仅代表尚未执行的任务,并不包括可能已经启动但尚未完成的任务。

示例代码:

ExecutorService executor = Executors.newFixedThreadPool(10);

// 提交任务到线程池

// ...

// 尝试立即关闭线程池
List<Runnable> notExecutedTasks = executor.shutdownNow();

// 处理未执行的任务
for (Runnable task : notExecutedTasks) {
    // 可以选择重新提交任务或其他操作
}

使用 shutdownNow() 后,线程池将不再接受新的任务。如果需要等待正在执行的任务完成,通常会在调用 shutdownNow() 之后调用 awaitTermination() 方法来等待线程池中的所有任务完成执行。

1.2.awaitTermination

Java中 ExecutorService 接口的一个方法,用于请求关闭线程池,并等待所有提交的任务完成执行,或者直到发生超时或当前线程中断。
这个方法通常用于确保程序在终止前,线程池中的任务都已经完成执行。

awaitTermination 方法的如下:

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

参数说明:

  • timeout:最长等待时间。
  • unit:时间单位,是一个枚举类型 TimeUnit,可以是秒、毫秒等。

返回值:

  • 返回一个布尔值,如果线程池在指定的超时时间内关闭,则返回 true;如果超时时间到了但线程池还没有关闭,则返回 false

示例代码:

ExecutorService executorService = Executors.newFixedThreadPool(10);

// 提交任务到线程池
executorService.submit(() -> {
    // 任务代码
});

// 请求关闭线程池,不再接受新任务
executorService.shutdown();

try {
    // 等待线程池终止,等待最长60秒
    if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
        // 超时后尝试强制关闭
        executorService.shutdownNow();
    }
} catch (InterruptedException e) {
    // 当前线程被中断操作
    executorService.shutdownNow();
    // 重新中断当前线程
    Thread.currentThread().interrupt();
}

在上述代码中,首先通过 shutdown 方法发起关闭线程池的请求,然后通过 awaitTermination 方法等待线程池中的任务执行完毕或者等待60秒。如果60秒内线程池关闭,则继续执行后续操作;如果60秒后线程池仍然没有关闭,则通过 shutdownNow 方法尝试立即关闭线程池。如果等待过程中线程被中断,则会捕获 InterruptedException 异常,并通过 shutdownNow 方法立即关闭线程池。

2.jvm的关机挂钩

2.1.addShutdownHook

是Java提供的用于关闭钩子(Shutdown Hook)的方法。当JVM开始关闭过程时(通常是因为程序正常退出或因为接收到了系统的关闭信号),这些添加的钩子会被启动并执行
以下是这个方法的一些关键点:

  • 关闭钩子是一种初始化但未启动的线程,当JVM开始关闭过程时,系统会自动启动这些线程。
  • 你可以添加多个关闭钩子。在JVM关闭时,它们将并发执行,所以关闭钩子之间不应存在依赖或同步要求。
  • 一旦虚拟机开始执行关闭钩子,它将等待所有的钩子执行完毕才会进一步关闭。因此,关闭钩子的执行时间不应过长,以免延迟程序的关闭过程。
  • 关闭钩子可以用于清理资源,例如关闭文件句柄、网络连接或释放其他系统资源。
  • 在某些情况下,JVM可能不会执行关闭钩子,例如如果JVM是由于用户中断(如按下Ctrl+C)或系统级别的错误导致急速关闭。
    示例代码:
// 创建一个新的线程作为Shutdown Hook
Thread shutdownHook = new Thread() {
    public void run() {
        System.out.println("Shutdown hook is running !");
        // 这里可以放置清理资源的代码
    }
};

// 添加Shutdown Hook
Runtime.getRuntime().addShutdownHook(shutdownHook);

在上述示例中,我们创建了一个匿名内部类的线程,重写了其 run 方法以打印一条信息。这个线程被作为一个关闭钩子添加,当JVM关闭时,这个线程会被执行。

3.完整实现


@Service
public class Executors{
	// 在构造方法中声明--项目启动时就会执行
	static {
        // 声明一个thread,书写具体功能实现
        Thread thread = new Thread(() -> {
            log.info("JVM接受到关闭命令,等待线程池运行完剩余任务后结束线程池............");
            // 请求关闭线程池,不再接受新任务
			executorService.shutdown();
			
			try {
			    // 等待线程池终止,等待最长60秒
			    if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
			        // 超时后尝试强制关闭
			        executorService.shutdownNow();
			    }
			} catch (InterruptedException e) {
			    // 当前线程被中断操作
			    executorService.shutdownNow();
			    // 重新中断当前线程
			    Thread.currentThread().interrupt();
			}
            log.info("线程池结束成功............");
        });
        // 声明为用户线程:意味着在JVM中,只要这个线程没有执行完毕,即便是其他用户线程都已经完成,JVM也不会退出
        thread.setDaemon(false);
        // 当机器被关机时,会执行该线程,直到执行完毕才会关机
        Runtime.getRuntime().addShutdownHook(thread);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值