当前位置: 首页 > 后端技术 > Java

codereview,瑞出事了!

时间:2023-04-02 01:38:40 Java

前不久,部门做了一次codereview。代码整体比较简单,该吹B的地方已经吹好了。无非是ifelse的一些老毛病。翻到定时任务的一步执行代码时,我眼前一亮,觉得是时候让BB说点什么了。没想到这群家伙在审稿的时候给了我满满的认可感,但是审稿没多久就给了我B的称号。今天我就把当时的这些话整理一下,让大家说说是否我是不是有问题。甘!一个任务处理示例代码的结构大致是这样的。通过定时,这段代码需要每天晚上和凌晨对数据库中的记录进行调和。主要逻辑是使用独立的线程逐步读取数据库中的相关记录,然后在循环中对这些记录进行一条一条的处理。ExecutorService服务=Executors.newFixedThreadPool(10);...service.submit(()->{while(true){if(CollectionUtils.isEmpty(items)){break;}}Listitems=start,queryPageData(end);//实用逻辑for(dataitem:items){try{thread.sleep(10L);}Catch(Internet(InternetExceptionE){//noop}ProcessIntem(ITEM);}});复制代码。马上翻代码的时候,我停了下来,这里的processItem并没有捕获到异常。正常情况下,这不是问题。但是平静的岁月总是偶尔会被一些随机的意外打断。如果这是你的完整代码任务,它有一个非常隐蔽的失败方式。即使你的单元测试写得很好,我们仍然可以通过远程中毒和问题记录来导致这段代码出现问题。是的。上面代码的根本原因是可能出现的异常processItem函数生成的对象不会被捕获。如果在记录处理过程中有任何item抛出异常,不管是checkedexception还是uncheckedexception,都会终止整个task的执行!别以为容易,踩过这个坑的同学请记得扣666。或者翻翻你的任务执行代码看看自己有没有这个问题。Java编译器在很多情况下会提示你捕获异常,但总有一些异常会逃逸,比如空指针异常。如下图所示,RuntimeException和Error都是uncheckedexceptions。RuntimeException不用try...catch也可以处理,但是如果发生异常,会导致程序的执行中断,JVM会统一处理这些异常。抓不到,自然也就结束了你的任务。如果要异步执行一些任务,不如在异常设计上多花点功夫。这车翻车的同学不少,这车不介意再带一辆。审稿人很谦虚,马上当场修改了代码。不要吞下原始异常并查看修改后的代码。ExecutorService服务=Executors.newFixedThreadPool(10);...service.submit(()->{while(true){if(CollectionUtils.isEmpty(items)){break;}}Listitems=start,queryPageData(End);//实用逻辑for(dataitem:items){try{thread.sleep(10L);}Catch(interruptedExceptionE){//noop}TRY{processitem(item);.error(...,例如);}}}}});...service.shutdownNow();复制代码为了控制任务执行的频率,sleep方法是一个有效的方法。代码很贴心,按照我们上面说的方式捕获了异常。同时,它也非常贴心地捕获睡眠相关的异常。这里要是不体贴也没办法,因为如果这部分代码没有完成的话,编译是不会通过的。让我们认为开发人员的水平不够好。由于sleep抛出InterruptedException,代码什么也不做。这也是我们代码中常见的操作。不信你打开你的项目,肯定有很多代码忽略了InterruptedException。此时,你去执行这段代码。虽然线程池使用了暴力的shutdownNow函数,但是你的代码还是不能终止,它还会继续运行。因为你忽略了InterruptedException异常。当然,我们可以在捕获到InterruptedException时终止循环。try{Thread.sleep(10L);}catch(InterruptedExceptione){break;}复制代码虽然这样可以达到预期效果,但是一般不会这样处理InterruptedException。正确的处理方式是这样的:while(true){ThreadcurrentThread=Thread.currentThread();if(currentThread.isInterrupted()){中断;}尝试{Thread.sleep(1L);e}catch(Interrupted)Exception{currentThread.interrupt();}}}除了捕获它,我们还要重新设置中断状态,否则它会随着捕获一起被清除。InterruptedException在很多场景下都非常重要。当某些方法一直阻塞线程时,比如耗时计算,整个线程就会卡在那里,什么也做不了。InterruptedException可以中断任务的执行,非常有用。但是对我们现在代码的逻辑没有任何影响。被评论的朋友不满意。还是有问题!有没有效果是一回事,是不是好习惯又是另一回事。我尝试尽可能地安装B。其实你的异常处理代码还有其他隐藏的问题。还有问题吗?,大家一改往日懒散的表情,该好好谈谈了。下面来看看小伙伴们现场改的问题。他这里直接用catch捕获了异常,然后记录了相应的日志。我想说的问题是这里的Exception粒度不对,太粗暴了。try{processItem(item);}catch(Exceptionex){LOG.error(...,ex);}复制代码processItem函数抛出IOException,也抛出InterruptedException,但是我们都把它当作普通的Exception,所以它不能体现上层函数抛出异常的意图。比如processItem函数抛出一个TimeoutExcepiton,希望我们可以根据它做一些重试;或者抛出SystemBusyExcption,希望我们可以睡一会儿,给服务器一些时间。这种粗粒度的异常一次捕获它们。当新增异常时,根本找不到这些代码,就会出现风险。一时间,会议室里鸦雀无声。我觉得你说的对。一位资深老手说,你的意思是把所有的异常情况单独抓取,进行细粒度的处理。但是最后还是得用Exception来捕获RuntimeException,异常还是捕获不到。这真是一个非凡的问题。优秀的、规范的代码写法,无法实现的重要因素之一就是项目中的其他代码根本不按规矩办。如果我们的下层代码进行了正确的空指针判断,数组越界操作,或者使用了Guava的Preconditions等API进行了异常的预翻译,以上问题根本不需要回答。但是在上面代码的情况下,我们需要手动捕获RuntimeException并单独处理。在你的项目中,坏代码太多,不好改。我虽然有情商,但是脾气比较大。大家分手了。完了我实在想不通,codereview是用来找问题的。结果这篇评论一开,大家都在背后讽刺我。我的问题到底是什么?还是团队的问题?人们不明白。当你纠结于使用Integer还是int时,我什么也没说。下面说说异常处理,玻璃心到受不了。你不能假装所有这些B。什么?你想审查我的代码吗?看看我是不是像我说的那样写代码,我是否以身作则?不好意思,我是架构师,很多年没写过代码了。你这个愿望落空了!