Java多线程——<七>多线程的异常捕捉

本文介绍了Java中多线程异常的捕捉方法。通过自定义异常处理器并设置到线程工厂,可以有效捕捉并处理多线程运行时抛出的异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

  为什么要单独讲多线程的异常捕捉呢?先看个例子:

public class ThreadException implements Runnable{
    @Override
    public void run() {
        throw new RuntimeException();
    }
    //现象:控制台打印出异常信息,并运行一段时间后才停止
    public static void main(String[] args){
        //就算把线程的执行语句放到try-catch块中也无济于事
        try{
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(new ThreadException());
        }catch(RuntimeException e){
            System.out.println("Exception has been handled!");
        }
    }
}

  在run中手动抛出了一个运行时异常,在main中启动线程,catch语句块中捕捉下异常,捕捉到打印一句话。运行结果如下图:

  发现异常被抛到了控制台,没有打印catch块中的语句。

  结论:多线程运行不能按照顺序执行过程中捕获异常的方式来处理异常,异常会被直接抛出到控制台(由于线程的本质,使得你不能捕获从线程中逃逸的异常。一旦异常逃逸出任务的run方法,它就会向外传播到控制台,除非你采用特殊的形式捕获这种异常。),这样会让你很头疼,无法捕捉到异常就无法处理异常而引发的问题。

  于是,我们一定会想如何在多线程中捕捉异常呢?

二、多线程中捕捉异常

  我们来按照下面的步骤完成这次实验:

  1.定义异常处理器

   要求,实现 Thread.UncaughtExceptionHandler的uncaughtException方法,如下:

/*
 * 第一步:定义符合线程异常处理器规范的“异常处理器”
 * 实现Thread.UncaughtExceptionHandler规范
 */
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
    /*
     * Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用
     */
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("caught    "+e);
    }
}

 

  2.定义使用该异常处理器的线程工厂

/*
 * 第二步:定义线程工厂
 * 线程工厂用来将任务附着给线程,并给该线程绑定一个异常处理器
 */
class HanlderThreadFactory implements ThreadFactory{
    @Override
    public Thread newThread(Runnable r) {
        System.out.println(this+"creating new Thread");
        Thread t = new Thread(r);
        System.out.println("created "+t);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());//设定线程工厂的异常处理器
        System.out.println("eh="+t.getUncaughtExceptionHandler());
        return t;
    }
}

  3.定义一个任务,让其抛出一个异常

/*
 * 第三步:我们的任务可能会抛出异常
 * 显示的抛出一个exception
 */
class ExceptionThread implements Runnable{
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run() by "+t);
        System.out.println("eh = "+t.getUncaughtExceptionHandler());
        throw new RuntimeException();
    }
}

  4.调用实验

/*
 * 第四步:使用线程工厂创建线程池,并调用其execute方法
 */
public class ThreadExceptionUncaughtExceptionHandler{
    public static void main(String[] args){
        ExecutorService exec = Executors.newCachedThreadPool(new HanlderThreadFactory());
        exec.execute(new ExceptionThread());
    }
}

  运行结果如下图:

三、结论

  在java中要捕捉多线程产生的异常,需要自定义异常处理器,并设定到对应的线程工厂中(即第一步和第二步)。

四、拓展

  如果你知道将要在代码中处处使用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获处理器。

这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。

public static void main(String[] args){
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        ExecutorService exec =Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
}

注:以上代码均来自《thinking in java》,内容均是自己总结,如有错误,欢迎大家批评指正

 

转载于:https://www.cnblogs.com/brolanda/p/4725138.html

### Java 多线程环境下的事务回滚机制 在Java多线程环境中实现事务回滚主要依赖于框架的支持以及合理的编程模式。对于Spring框架而言,在多线程环境下确保事务一致性至关重要,因为并发执行可能导致事务属性受损[^2]。 #### 使用 `@Transactional` 注解控制事务边界 为了简化开发并提高可读性,推荐使用Spring提供的`@Transactional`注解来声明服务层方法的事务行为: ```java @Service public class OrderService { @Autowired private OrderRepository orderRepo; @Transactional(rollbackFor = Exception.class) public void placeOrder(Order order) throws Exception { try { // 执行订单创建逻辑... orderRepo.save(order); // 模拟异常情况触发回滚 if (order.getPrice() < 0) throw new IllegalArgumentException("价格非法"); } catch (Exception e) { log.error("发生错误, 将会回滚", e); throw e; // 抛出异常以激活回滚机制 } } } ``` 上述代码片段展示了如何利用`@Transactional`指定哪些类型的异常应该导致事务自动回滚。这里配置了任何继承自`Exception`类别的未捕获异常都会引起整个操作被撤销[^5]。 #### 结合 `PlatformTransactionManager` 进行手动管理 除了依靠注解外,还可以借助`PlatformTransactionManager`接口及其子类来进行更细粒度的手动控制。这允许开发者显式地获取、提交或回滚事务状态: ```java @Autowired private PlatformTransactionManager transactionManager; @Transactional(propagation = Propagation.NOT_SUPPORTED) public void processWithManualTxControl() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { // ... business logic ... transactionManager.commit(status); // 成功则提交 } catch (RuntimeException ex) { transactionManager.rollback(status); // 出错即回滚 throw ex; } } ``` 此方式特别适用于那些需要跨多个数据源或其他资源的操作场景下保持一致性的场合[^4]。 #### 利用 AOP 实现全局异常捕捉与统一回滚策略 面向切面编程(AOP)可以帮助建立一套通用的异常处理流程,使得即使是在异步任务或多线程调度中也能有效地实施回滚措施。下面是一个简单的AOP配置例子: ```xml <aop:config> <aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/> </aop:config> <tx:advice id="txAdvice"> <tx:attributes> <!-- 对所有匹配的服务方法启用事务支持 --> <tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/> </tx:attributes> </tx:advice> ``` 这段XML定义了一个切入点用于拦截所有的服务层方法调用,并为它们附加了一条建议——每当遇到任意抛出的`Throwable`都将强制回滚当前事务[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值