线程池中的线程抛异常问题

线程抛异常问题

execute方法中的run方法抛出异常

@Slf4j
@Service
public class MenuServiceImpl {

    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    public List<MenuDTO> listMenu() throws Exception {
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                log.info("==================== 进入execute的run方法");
                int i = 1 / 0;
            }
        });

        List<MenuEntity> menuEntityList = menuMapper.selectMenuList();
        log.info("==================== menuEntityList : {}", menuEntityList);
        return CopyUtils.copyListByShallow(menuEntityList, MenuDTO.class);
    }
}

结果:

image

说明:
可以看到execute方法执行的线程,抛出了计算异常,控制台打印了异常信息,但是主线程仍然可以继续执行。

submit方法中的run方法抛出异常

@Slf4j
@Service
public class MenuServiceImpl {

    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    public List<MenuDTO> listMenu() throws Exception {

        Future<Integer> future = threadPoolExecutor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.info("==================== 进入submit的run方法");
                int i = 1 / 0;
                return i;
            }
        });

        List<MenuEntity> menuEntityList = menuMapper.selectMenuList();
        log.info("==================== menuEntityList : {}", menuEntityList);

        future.get();
        return CopyUtils.copyListByShallow(menuEntityList, MenuDTO.class);
    }
}

结果:

image

说明:
当调用future.get();方法的时候,将会抛出异常,而如果未调用get方法,那么将不会抛出异常。

异常处理

方法1,使用try-catch处理异常

threadPoolExecutor.execute(new Runnable() {
	@Override
	public void run() {
		try {
			log.info("==================== 进入execute的run方法");
			int i = 1 / 0;
		} catch (Exception e) {
			log.info("==================== 1 / 0 异常 : " + e);
		}

	}
});

image

方法2,使用Thread.setDefaultUncaughtExceptionHandler方法处理异常

方法1可以捕获异常,但是每段代码都写try-catch实在是太麻烦了,在创建线程时可以用Thread.setDefaultUncaughtExceptionHandler方法捕获异常。

@Bean
@Scope("singleton")
public ThreadPoolExecutor getExecutor() {
	ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
			keepAliveSeconds, TimeUnit.SECONDS,
			new LinkedBlockingQueue<>(queueCapacity), new InnerThreadFactory(),
			new ThreadPoolExecutor.CallerRunsPolicy() {
				@Override
				public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
					if (!e.isShutdown()) {
						r.run();
					}
				}
			});
	executor.allowCoreThreadTimeOut(allowCoreThreadTimeOut);
	return executor;
}

static class InnerThreadFactory implements ThreadFactory {
	@Override
	public Thread newThread(Runnable r) {
		Thread thread = new Thread(r);
		LocalDateTime ldt = LocalDateTime.now();
		DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyyMMd HH:mm:ss");
		// 设置线程名称
		thread.setName(THREAD_PREFIX + ldt.format(pattern));
		// 设置线程的异常处理
		thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
			@Override
			public void uncaughtException(Thread t, Throwable e) {
				String msg = "线程ID : " + t.getId() +
						" 线程名 : " + t.getName() +
						" 异常 : " + e;
				log.info(msg);
			}
		});

		return thread;
	}
}

image

  • 当使用submit提交的时候,使用Thread.setDefaultUncaughtExceptionHandler方法却无法捕获异常
Future<Integer> future = threadPoolExecutor.submit(new Callable<Integer>() {
	@Override
	public Integer call() throws Exception {
		log.info("==================== 进入submit的run方法");
		int i = 1 / 0;
		return i;
	}
});

future.get();

image

原因 - submit源码:

public <T> Future<T> submit(Callable<T> task) {
	if (task == null) throw new NullPointerException();
	RunnableFuture<T> ftask = newTaskFor(task);
	execute(ftask);
	return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
	return new FutureTask<T>(callable);
}

image

image

get方法:

image

又把outcome存放异常对象搬出来了:

image

方法3,afterExecute方法,钩子方法,重写afterExecute方法

注意,由于submit方法会吞噬异常,所以重写afterExecute方法生效,只会对execute方法有效。

image

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
		keepAliveSeconds, TimeUnit.SECONDS,
		new LinkedBlockingQueue<>(queueCapacity), new InnerThreadFactory(),
		new ThreadPoolExecutor.CallerRunsPolicy() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
				if (!e.isShutdown()) {
					r.run();
				}
			}
		}) {
	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		String msg = "线程池报异常 : " + t;
		log.info(msg);
	}
};

image

posted @ 2023-06-03 22:29  sunpeiyu  阅读(90)  评论(0编辑  收藏  举报