后台开发笔记

压测踩坑--线程池中操作异常

2021-06-08  本文已影响0人  Figo_OU

最近在做压测,对于一些rt(响应时间)比较高的接口做了一些优化。后面找时间一起总结一下。

因为要减少rt,所以在操作数据库的时候用到了线程池的方式来并发地进行数据库的操作。给出方案后是另一个同事实现的。
结果后面看相关的代码,不对呀,异常处理都没有做的呀。具体代码如下。洋洋洒洒写了一大堆。

//数据库写操作A
 executorService.submit(()->{
    //数据库查询
    //...
    latch.countDown();
});

executorService.submit(()->{
    //数据库查询
    latch.countDown();
});

executorService.submit(()->{
    //数据库查询
    latch.countDown();
});

executorService.submit(()->{
    //数据库查询
    latch.countDown();
});

我和他说,
“你这异常处理没有做呀,去搞一下把,你这出异常没办法回滚呀。”

结果他给我开始背起了八股文
“execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;”
“submit()方法用于提交需要返回值的任务。所以我们一般用submit,不用execute。”

我也不想和他bb,“你就直接看看如果异常你这代码会不会回滚。”

半小时后过来和我说,
“好像回滚不了呀;换了用execute是能看到错误信息,但回滚也还是不行。你这线程池减少rt的方案不行呀。”

我吐了我,八股文不能背全吗?。submit()会返回一个 Future 类型的对象,并且可以通过 Future 的 get()方法来获取返回值。只要try\catch掉 submit.get();如果线程池中有异常,数据库写操作A就会回滚拉。
解决后代码。

//数据库写操作A
Future<?> submit = executorService.submit(() -> {
    //业务代码
});

try {
    submit.get(); 
} catch (InterruptedException e) {
    throw SystemBaseExceptionEnum.COMMON_ERROR.getException("msg1");
} catch (ExecutionException e) {
    throw SystemBaseExceptionEnum.COMMON_ERROR.getException("msg2");
}

后面还得找了一个相关的文章
https://blog.csdn.net/zangdaiyang1991/article/details/89228103

看来八股文还得背。

既然进来了,也绝不能让各位吃亏。
有没有想过一个问题,能否将写操作也放到线程池里面去跑呢?这不是更快吗?

答案是不行的。

假设代码如下:
场景一:

Future<?> submit1 = executorService.submit(() -> {
    //删除项目id(普通索引)为10086的表A数据。事务A
});

Future<?> submit2 = executorService.submit(() -> {
    //往表A添加项目id为10086数据 ,
});
//这样会发生死锁。原因应该是不同线程操作同一个表,然后之前删除数据后,产生了间隙锁。导致再往A添加数据的时候就会发生死锁/

场景二:

// 事务1,成功
Future<?> submit1 = executorService.submit(() -> {
    // 事务2,成功
});

Future<?> submit2 = executorService.submit(() -> {
    // 事务3,失败。
});

try {

    submit1.get();
    submit2.get();  
} catch (InterruptedException e) {
    throw e;
} catch (ExecutionException e) {
    throw e;
}
//这样事务1能回滚,但事务2不能回滚。

所以!不要把写操作放到线程池中跑。

上一篇下一篇

猜你喜欢

热点阅读