美文网首页
如何主动停止线程池中的一个线程?

如何主动停止线程池中的一个线程?

作者: CoderInsight | 来源:发表于2023-08-27 17:52 被阅读0次

    1),停止?or 取消?

    在使用线程池进行异步任务执行时,通常情况下不会直接停止线程池中正在运行的线程。相反,我们可以采用一种更加协调和优雅的方式来处理这个需求,即通过取消任务的方式来停止线程池中的某个正在运行的线程。

    1. 停止(中断)线程:我们知道可以使用 interrupt 方法来停止一个线程,其也有自己的局限性,由于中断线程的底层方法是将interrupted()的属性值设置为ture

      • (1),所以对于非阻塞的线程,只是改变了中断状态为 ture,具体的逻辑还需要被中断的的线程自己处理。即如果线程自己不检查中断状态或者不进行相应的处理,那么interrupt 方法本身并不会立即终止线程的执行。
      • (2),而对于阻塞中的线程,在接收到的中断信号之后会抛出异常(InterruptedException),并将中断状态设置为ture
        • 这里说的阻塞状态包含:sleep()wait()等,其都会立即抛出异常并清除中断状态。
        • 其他阻塞状态,如IO操作、LockBlockingQueue等,也会抛出异常并结束线程的阻塞状态。
        • 通过分析其源码也可以得出相同的结论,如下是对应的伪代码:
        public static void sleep(long millis) throws InterruptedException {
            // 检查中断状态
            if (Thread.interrupted()) {
                // 抛出InterruptedException异常并清除中断状态
                throw new InterruptedException();
            }
            
            // 调用底层平台的休眠方法
            // ...
        }
        
      • (3),注意,在捕获到InterruptedException异常后,线程可以根据自身的逻辑进行相应的处理,例如恢复中断状态、执行清理操作、终止线程等。
        • 1),interrupted() 方法却能改变interrupted的属性值;
        • 2),isInterrupted()方法不能改变interrupted的属性值,可以用来判断线程是否被中断了,从而可以通过这个判断进行额外的逻辑处理。
    2. 取消线程:这个概念是出现在线程池中的,即如果想中断线程池中的一个线程可以使用t.cancel(true)去实现,在其方法内部中通过t.interrupt();便可以实现线程中断。所以值的注意的是无论是哪种方式都会归结到 interrupt 方法,而这个方法本身是无法中断正在运行的线程的,而是要配合一个额外的逻辑处理。

    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ThreadPoolManager {
    
        private ThreadPoolTaskExecutor taskExecutor;
    
        public ThreadPoolManager() {
            taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.initialize();
        }
    
        public void executeTask(String taskId) {
            taskExecutor.submit(() -> {
                // 异步任务的逻辑
                // 可能是一个长时间运行的任务
    
                // 判断任务是否被取消
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Task " + taskId + " is canceled.");
                    return;
                }
    
                // 执行任务逻辑
                System.out.println("Executing task " + taskId);
                try {
                    Thread.sleep(5000);  // 模拟耗时操作
                } catch (InterruptedException e) {
                    System.out.println("Task " + taskId + " is interrupted.");
                    return;
                }
                System.out.println("Task " + taskId + " completed.");
            });
        }
    
        public void cancelTask(String taskId) {
            // 模拟取消任务
            for (Runnable runnable : taskExecutor.getThreadPoolExecutor().getQueue()) {
                if (runnable instanceof ThreadPoolTask) {
                    ThreadPoolTask threadPoolTask = (ThreadPoolTask) runnable;
                    if (threadPoolTask.getTaskId().equals(taskId)) {
                        threadPoolTask.cancel();
                        System.out.println("Task " + taskId + " is requested to cancel.");
                        break;
                    }
                }
            }
        }
    }
    

    2),@Async注解与CompletableFuture

    结论:在Spring框架中,无法直接取消或移除线程池中正在执行的特定任务。@Async 注解将方法提交给线程池后,无法直接操作线程池中的具体任务。

    1. 我们可以增加一个判断标志变量并定期检查该变量的值。当标志变量为指定的取消状态时,提前退出循环以取消任务。示例代码如下所示:
    @Async("asyncExecutor")
    public void asyncUpdateUserName(List<UserVo> userVoList) {
        log.info("异步更新用户名");
        // 取消标志变量
        boolean cancelled = false;
        for (UserVo userVo : userVoList) {
            if (cancelled) {
                log.info("任务被取消,提前退出循环");
                break;
            }
            userService.usedateUserName(userVo.getId(), userVo.getName());
        }
    }
    // 取消任务的示例代码:根据合适的条件下设置变量的结果值
    // 设置取消标志变量为true
    boolean cancelled = true;  
    
    1. 使用Future对象:修改asyncUpdatePrintTopology方法的返回类型为Future<?>,并在调用该方法时获取返回的Future对象。然后,在需要取消任务的时候,调用Future对象的cancel()方法进行取消操作。示例代码如下所示:
    @Async("asyncExecutor")
    public Future<?> asyncUpdateUserName(List<UserVo> userVoList) {
        log.info("异步更新用户名");
        for (UserVo userVo : userVoList) {
            userService.usedateUserName(userVo.getId(), userVo.getName());
        }
        return new AsyncResult<>(null);
    }
    
    // 取消任务的示例代码
    Future<?> future = asyncUpdateUserName(userVoList);
    future.cancel(true);  // true表示是否允许正在运行的任务中断
    
    

    使用Future对象的优点是可以实现较为简单的任务取消操作,但无法强制终止正在执行的任务。

    相关文章

      网友评论

          本文标题:如何主动停止线程池中的一个线程?

          本文链接:https://www.haomeiwen.com/subject/qsuwmdtx.html