java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot线程池异常处理

Springboot线程池异常处理的实现示例

作者:sjsjsbbsbsn

在使用线程池时,异常处理可能会被忽略,从而导致潜在的程序问题甚至崩溃,本文主要介绍了Springboot线程池异常处理的实现示例,感兴趣的可以了解一下

在 Java 多线程编程中,线程池(ThreadPoolExecutor)是一个常用的工具,用于管理线程的生命周期并提升应用程序的性能。然而,在使用线程池时,异常处理可能会被忽略,从而导致潜在的程序问题甚至崩溃。如果任务出现了异常,会发生什么呢?该怎么处理呢?怎么获取到异常信息来解决异常?想要知道如何解决,就需要了解了解线程池提交任务的两个方法executesubmit

一.execute与submit

package demo1;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test2 {
    public static void main(String[] args) {
        //只有一个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(1);
        threadPool.execute(()->{
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            method_1();
        });
        Future<?> submit = threadPool.submit(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            method_1();
        });
        
        Future<?> submit2 = threadPool.submit(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            method_1();
        });
    }

    public static String method_1(){
        System.out.println("starting.....");
        int i = 1/0;
        System.out.println("ending.....");
        return "ok";
    }
}

这里我executesubmit 分别执行两个有异常的任务,同时打印了当前线程,以下是运行结果

在这里插入图片描述

可见execute方法在遇到异常之后会抛出异常,并且线程池中的线程终止

submit方法遇到异常不会抛出异常

特性executesubmit
方法定义void execute(Runnable command)Future<?> submit(Runnable task)Future<T> submit(Callable<T> task)
返回值无返回值,不关心任务的执行结果返回 Future 对象,可用于获取任务结果或状态
支持任务类型仅支持 Runnable支持 Runnable 和 Callable
异常处理任务抛出的异常不会被捕获,直接传播到线程池的工作线程任务抛出的异常会被封装在 Future 中,需通过 Future.get() 获取
使用场景适用于无需获取任务结果的场景适用于需要获取任务执行结果或捕获异常的场景
示例threadPool.execute(() -> method_1());Future<?> future = threadPool.submit(() -> method_1());
任务执行方式直接提交给线程池执行包装为 FutureTask 后交由线程池执行
线程池依赖线程池的 execute 方法是基础实现submit 方法内部调用 execute 执行任务
异常传播位置通过默认的 UncaughtExceptionHandler 处理通过 Future.get() 抛出异常

二.如何处理异常

2.1使用try-catch

这里不多赘述,这是最简单明了的方法,直接用try-catch捕获就行

2.2 使用 ThreadPoolExecutor 的 afterExecute 方法

ThreadPoolExecutor 提供了一个 afterExecute 钩子方法,可以在任务完成后检查是否有异常。
通过覆盖此方法,可以捕获所有任务中未被捕获的异常。

package demo1;

import java.io.IOException;
import java.util.concurrent.*;

public class Test3 {
    public static void main(String[] args) {
        ExecutorService threadPool = new /**
         * @author 方
         */
        ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                if (t != null) {
                    System.out.println("Task threw an exception: " + t.getMessage());
                }
                // 针对 Future 的异常处理
                if (r instanceof Future<?>) {
                    try {
                        ((Future<?>) r).get(); // 调用 get 检查任务是否抛出异常
                    } catch (Exception e) {
                        System.out.println("Exception in Future: " + e.getCause());
                    }
                }
            }
        };

        threadPool.execute(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            throw new RuntimeException("Test1 exception in execute");
        });
        threadPool.submit(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            throw new RuntimeException("Test2 exception in submit");
        });
        threadPool.submit(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            throw new RuntimeException("Test3 exception in submit");
        });
        threadPool.shutdown();

    }
}

运行结果:

在这里插入图片描述

可见execute方法在遇到异常之后会抛出异常,并且线程池中的线程终止,submit没有抛出异常

但是他们两个**都记录了异常信息**

2.3设置 UncaughtExceptionHandler

package demo1;

import java.io.IOException;
import java.util.concurrent.*;

public class Test3 {
    public static void main(String[] args) {
        //1.实现一个自己的线程池工厂
        ThreadFactory factory = (Runnable r) -> {
            //创建一个线程
            Thread t = new Thread(r);
            //给创建的线程设置UncaughtExceptionHandler对象 里面实现异常的默认逻辑
            t.setDefaultUncaughtExceptionHandler((Thread thread1, Throwable e) -> {
                //出现异常
                if (e != null){
                    System.out.println(Thread.currentThread().getName()+e.getMessage());
                    e.printStackTrace();
                }
            });
            return t;
        };

        //2.创建一个自己定义的线程池,使用自己定义的线程工厂
        ExecutorService threadPool = new ThreadPoolExecutor(
                1,
                1,
                0,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue(10),
                factory);

        threadPool.execute(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            throw new RuntimeException("Test1 exception in execute");
        });
        threadPool.submit(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            throw new RuntimeException("Test2 exception in submit");
        });
        threadPool.submit(() -> {
            System.out.println("Execute: Current thread is " + Thread.currentThread().getName());
            throw new RuntimeException("Test3 exception in submit");
        });
        threadPool.shutdown();

    }
}

运行结果:

在这里插入图片描述

可见execute方法在遇到异常之后会抛出异常,并且线程池中的线程终止,submit没有抛出异常

三.Springboot中的线程池异常处理

   @Bean(MALLCHAT_EXECUTOR)
    @Primary
    public ThreadPoolTaskExecutor mallchatExecutor() {
        //spring的线程池
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //线程池优雅停机的关键
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("mallchat-executor-");
        //拒绝策略->满了调用线程执行,认为重要任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //自己就是一个线程工程
        executor.setThreadFactory(new MyThreadFactory(executor));
        executor.initialize();
        return executor;
    }
package org.fth.mallchat.common.common.thread;

import lombok.AllArgsConstructor;

import java.util.concurrent.ThreadFactory;

/**
 * @author 方
 */
@AllArgsConstructor
public class MyThreadFactory implements ThreadFactory {

    private static final MyUncaughtExceptionHandler MyUncaughtExceptionHandler = new MyUncaughtExceptionHandler();
    private ThreadFactory original;

    @Override
    public Thread newThread(Runnable r) {
        //执行Spring线程自己的创建逻辑
        Thread thread = original.newThread(r);
        //我们自己额外的逻辑
        thread.setUncaughtExceptionHandler(MyUncaughtExceptionHandler);
        return thread;
    }
}

package org.fth.mallchat.common.common.thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 方
 */
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(MyUncaughtExceptionHandler.class);

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        log.error("Exception in thread",e);
    }
}

1. 线程池配置 (ThreadPoolTaskExecutor)

在 Spring Boot 中,ThreadPoolTaskExecutor 用于管理线程池的执行,允许我们设置线程池的核心大小、最大线程数、队列容量等。通过这种配置,我们可以控制线程池的资源使用情况,确保任务的执行效率与可靠性。在你的代码中,线程池的配置包括:

2. 自定义线程工厂 (ThreadFactory)

通过 ThreadFactory,你可以自定义线程的创建过程。在你的代码中,你为每个线程设置了一个异常处理器。这意味着,如果线程内发生未捕获的异常,这些异常会被专门的异常处理器捕获并记录,而不是导致线程崩溃或丢失异常信息。

3. 线程销毁与异常

线程池的行为与异常处理相关:

因此,线程池不会因单个线程的异常而销毁整个线程池,它会继续运行,并且通过异常处理机制记录异常,确保系统的稳定性。

现未捕获的异常时,异常都能被记录。这个处理器的作用是将异常信息输出到日志中,避免错误被忽略或导致线程不可控。

不过sumbit还是必须要get才能拿到异常信息,我们还是可以通过重写ThreadPoolExecutor 的 afterExecute 方法 不过这样的话就有点麻烦

到此这篇关于Springboot线程池异常处理的实现示例的文章就介绍到这了,更多相关Springboot线程池异常处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文