子线程任务发生异常时主线程事务回滚示例过程

 更新时间:2022年03月15日 08:49:47   作者:字母哥哥  
这篇文章主要为大家介绍了子线程任务发生了异常时主线程事务如何回滚的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步

一、提出问题

最近有一位朋友问了我这样一个问题,问题的截图如下:

在这里插入图片描述

这个问题问的相对比较笼统,我来稍微详细的描述下:主线程向线程池提交了一个任务,如果执行这个任务过程中发生了异常,如何让主线程捕获到该异常并且进行事务的回滚。

二、主线程与子线程

先来看看基础,下图体现了两种线程的运行方式,

左侧的图,体现了主线程启动一个子线程之后,二者互不干扰独立运行,生死有命,从此你我是路人!

右侧的图,体现了主线程启动一个子线程之后继续执行主线程程序逻辑,在某一节点通过阻塞的方式来获取子线程的执行结果。

在这里插入图片描述

对于上文中提出的问题,一定是第二种才能解决主线程能够捕获子线程执行过程中发生的异常。这里就不得不提一个面试题,实现线程的两个接口Callable与Runnable之间的区别:

public interface Callable<V> {
    V call() throws Exception;
}
public interface Runnable {
    public abstract void run();
}

可以看到call方法带返回值,run方法没有返回值。另外call方法可以抛出异常,run方法不可以。很明显,我们为了要捕获或得知子线程的运行结果,或者运行异常,都应该通过Callable接口来实现。

这里我们写一个ExpSubThread类(子线程异常模拟类),实现Callable接口,不做过多的动作,直接抛出一个空指针异常。

public class ExpSubThread implements Callable {
    @Override
    public Object call() throws Exception {
        throw new NullPointerException();
    }
}

三、线程池

在面临线程任务时,通常我们会预先建立一个线程池,线程池是预先规划好的n个线程资源的集合。它的好处在于:

执行任务时,不是新建一个线程,而是使用线程池内已有的线程资源。任务执行完成也不是销毁线程,而是将线程资源归还线程池。所以在一定程度上,节省了线程创建和销毁所消耗的资源,达到线程资源重复利用的目的。

因为线程池创建的大小是有上限的,所以线程池还有另外的一个作用就是避免线程无限制的被创建,避免应用资源无限制的被占用导致的系统宕掉的问题。

常用的线程池有两种,一种是JDK自带的,一种是Spring线程池,在Spring环境下后者常常被使用,二者大同小异。这里我们使用Spring API来构建一个线程池。

public ThreadPoolTaskExecutor getThreadPool(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(100);  //线程池最大线程数
        executor.setCorePoolSize(50);//线程池核心线程数
        executor.setQueueCapacity(50);//任务队列的大小
        executor.setThreadNamePrefix("test_"); //线程前缀名
        executor.initialize(); //线程初始化
        return executor;
}

四、异常的捕获

下面是我写的一个测试用例,在这里它代表了主线程的程序执行流程

@Test
void subThreadExceptionTest() {
        try{
            //新建子线程对象
            ExpSubThread expSubThread = new ExpSubThread();
            //构建线程池
            ThreadPoolTaskExecutor executor = getThreadPool();
            //提交子线程任务,submit方法
            Future future = executor.submit(expSubThread);
            //在这里可以做主线程的业务其他流程操作
            //阻塞等待子线程的执行结果
            Object obj = future.get();  
        }catch (Exception e){
            e.printStackTrace();
            //事务回滚
        }
}

这里需要注意的是使用submit方法提交子线程任务到线程池内执行。ThreadPoolTaskExecutor有两种执行线程任务的方法,一种是execute方法,一种是submit方法。

execute方法没有返回值,所以无法判断任务是否成功完成,对应的线程类实现Runnable接口。

submit方法有返回值,返回一个Future,对应的线程类实现Callable接口。

在这里插入图片描述

Future.get()方法达到了阻塞主线程的目的,从而可以判断子线程任务的执行结果,并且get方法可以抛出异常。

    V get() throws InterruptedException, ExecutionException;

下面这张图是上面的测试用例程序程序e.printStackTrace();的效果,从图中可以看到两个Exception异常,一个是我们在子线程任务中以模拟的方式主动抛出的空指针异常,另一个由于空指针引发的get方法抛出的ExecutionException。

在这里插入图片描述

五、事务的回滚

上文中大家已经看到我们通过

  • 线程类实现Callable接口,达到了获取线程返回值,或者异常抛出的目的。
  • submit可以提交线程任务到线程池,并且可以获得子线程执行结果的返回值Future。
  • Future的get()方法可以获取子线程执行信息,包括异常的抛出。

那么既然我们已经可以在主线程内感知或catch子线程的异常信息了,下一步主线程的事务回滚是不是就太简单了?

jdbc 就conn.rollback()实现事务的回滚

spring环境下使用@Transactional注解就可以了。

以上就是子线程任务发生异常,主线程事务如何回滚的详细内容,更多关于子线程任务发生异常主线程事务如何回滚的资料请关注脚本之家其它相关文章!

相关文章

  • Java中的@RequiredArgsConstructor注解详解

    Java中的@RequiredArgsConstructor注解详解

    这篇文章主要介绍了Java中的@RequiredArgsConstructor注解详解,@RequiredArgsConstructor是Lombok的一个注解,简化了我们对@Autowired书写,@RequiredArgsConstructor注解可以代替@Autowired注解,需要的朋友可以参考下
    2024-01-01
  • SpringBoot启动参数的实现

    SpringBoot启动参数的实现

    SpringBoot通过jar文件方式启动,配置可以通过启动参数进行覆盖,本文就来介绍一下SpringBoot启动参数的实现,感兴趣的可以了解一下
    2025-01-01
  • java 中遍历取值异常(Hashtable Enumerator)解决办法

    java 中遍历取值异常(Hashtable Enumerator)解决办法

    这篇文章主要介绍了java 中遍历取值异常(Hashtable Enumerator)解决办法的相关资料,用迭代器取值时抛出的异常:java.util.NoSuchElementException: Hashtable Enumerator ,需要的朋友可以参考下
    2017-08-08
  • java 异常捕获及处理案例详解

    java 异常捕获及处理案例详解

    这篇文章主要介绍了java 异常捕获及处理案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • Spring Boot 启动加载数据 CommandLineRunner的使用

    Spring Boot 启动加载数据 CommandLineRunner的使用

    本篇文章主要介绍了Spring Boot 启动加载数据 CommandLineRunner的使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-04-04
  • Java解析XML的四种方式

    Java解析XML的四种方式

    本文详细讲解了Java解析XML的四种方式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Java多线程Thread , Future , Callable , FutureTask的使用

    Java多线程Thread , Future , Callable ,

    本文主要介绍了Java多线程Thread , Future , Callable , FutureTask的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Spring中@Cacheable注解的使用详解

    Spring中@Cacheable注解的使用详解

    这篇文章主要介绍了Spring中@Cacheable注解的使用详解,Spring框架提供了@Cacheable注解来轻松地将方法结果缓存起来,以便在后续调用中快速访问,本文将详细介绍@Cacheable注解的使用方法,并从源码级别解析其实现原理,需要的朋友可以参考下
    2023-11-11
  • Java基础详解之内存泄漏

    Java基础详解之内存泄漏

    这篇文章主要介绍了Java基础详解之内存泄漏,文中有非常详细的代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-04-04
  • Java中冒泡排序的原生实现方法(正序与逆序)

    Java中冒泡排序的原生实现方法(正序与逆序)

    这篇文章主要给大家介绍了关于Java中冒泡排序的原生实现方法(正序与逆序)的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11

最新评论