线程池+CountDownLatch 使用过程中遇到的坑和当前的疑问

tips:如果想要了解线程池和程序计数器可以参考上一篇文章。

synListItem = Collections.synchronizedList(new ArrayList<>());
                //创建一个CountDownLatch类,构造入参线程数
                CountDownLatch countDownLatch = new CountDownLatch(listItem.size());
                Iterator<Map<String, Object>> iterator = listItem.iterator();
                final ExecutorService es = ExecutorFactory.getEs();
                while (iterator.hasNext()) {
                    // 线程满导致异常,程序计数器未执行
                    try {
                        es.submit(() -> {
                            Map<String, Object> map = iterator.next();
                            // linkIds 为空表示开关未配置
                            try {
                           
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                //该线程执行完毕-1
                                countDownLatch.countDown();
                            }
                        });
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //该线程执行完毕-1
                        countDownLatch.countDown();
                    }
                }
                try {
                    countDownLatch.await(200, TimeUnit.MILLISECONDS);
                    log.info("after menu onsale filter listItem size:{}", synListItem.size());
                    log.debug("after menu onsale filter listItem:{}", synListItem);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

需求:异步等待访问一个性能不那么好的网络I/O,服务的qps 达到400
这次主要着重于线程池 和 try catch finally 和 countDownLatch.countDown(); 讨论
1.遇到的第一个问题:
ExecutorService 创建线程池,第一次使用的时候使用Executors.newCachedThreadPool(); 缓存线程池去使用,上线后没有明显的报错,但是!我发现线程池的数据和线程的数量在急速增加!!!我明显慌了!这样下去可不行,然后就换成了newFixedThreadPool(n);但是发现一直显示线程满了。相似的例图如下:
在这里插入图片描述
思考:1.为什么线程池变成固定的会线程不够
2.每个请求new 一个线程池,是不是不够优雅,违背线程池全局管理的定义。
行动:1.变成全局线程池
2.遇到的第二个问题:
1.换成全局线程池后,发现错误的范围蔓延了!因为网络接口非常不稳定,且效率过差,导致线程一直等待,然后线程池满了,导致其他服务线程池满报错!
思考:1.会不会是线程池太小了
行动:线程池扩大
3.遇到的第三个问题:
1.还是会存在线程池满的问题,对于当前的问题,有种无力的感觉,这个时候就在想:md,这是线程池和这个服务接口的问题,我代码一点问题没有,突然思路打开了,是不是代码可以优化一下,至少没那么难看,()
**思考1.**为什么线程池报错导致500返回呢,按道理服务应该错误被吃掉,给个200然后返回为空得了,500错误解释不过去了
过程:review代码和日志的时候,发现就是es.submit 这一行爆的错,我想是不是这个错误没有catch 导致的,同时突然看到程序计数器 如果报错,是不是没有被减少! 那这会不会导致线程堵住??!
行动:果断的把try catch finally放在外面
思考2.:但是这个时候想,多个线程哎,这样写在外面合适嘛?
使用了大模型得到了回答
为了让这个代码片段更加完整和正确,您应该包含一个 catch 块来处理任何可能抛出的异常。此外,虽然在这个特定的 submit 调用中直接捕获异常可能不是必需的(因为 submit 方法本身不会抛出检查型异常,并且会返回一个 Future 对象,任何异常都将被存储在该 Future 对象中),但在 lambda 表达式内部可能会抛出异常,这取决于您的代码逻辑。
行动:在线程中也加了try catch finally

思考3:咦,await是不是可以加个等待时间,这样再兜个底?
行动: 果断加上

结果:经过多次上线(该行为极其不建议,本人个人情况,需要批评的)
最终线程
1线程满的问题解决了

但是还是有个问题:1.线程满了,但是服务就像漏斗一样如果网络I/O一直很慢,最终还是会导致线程满掉。所以后期是给这个网络接口加个缓存!(其实写完这个博客,缓存已经加上了)只是没什么好讲的,就没在这篇文章中。
优化修改Bug: 第一层取消 finally 计数器减1,将其转到catch中

synListItem = Collections.synchronizedList(new ArrayList<>());
                //创建一个CountDownLatch类,构造入参线程数
                CountDownLatch countDownLatch = new CountDownLatch(listItem.size());
                Iterator<Map<String, Object>> iterator = listItem.iterator();
                final ExecutorService es = ExecutorFactory.getEs();
                while (iterator.hasNext()) {
                    // 线程满导致异常,程序计数器未执行
                    try {
                        es.submit(() -> {
                            Map<String, Object> map = iterator.next();
                            // linkIds 为空表示开关未配置
                            try {
                           
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                //该线程执行完毕-1
                                countDownLatch.countDown();
                            }
                        });
                    } catch (Exception e) {
                        countDownLatch.countDown();
                        e.printStackTrace();
                    }
                }
                try {
                    countDownLatch.await(200, TimeUnit.MILLISECONDS);
                    log.info("after menu onsale filter listItem size:{}", synListItem.size());
                    log.debug("after menu onsale filter listItem:{}", synListItem);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

疑惑:1.双层try catch 真的好吗?

最终:
取消

 catch (Exception e) {
                       // countDownLatch.countDown();
                        e.printStackTrace();
                    }

countDownLatch.await(200,TimeUnit.MILLISECONDS);
思考:1.如果加上,子线程还是在执行的过程中,如果对象没有深拷贝会出现多线程问题

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值