不久之前,部门进行了一次代码评审。

代码整体比较简单,该吹B的地方都已经吹过了,无非是些if else的老问题而已。当翻到一段定时任务的一步执行代码时,我的双眼一亮,觉得该BB两句了。

谁知这群家伙,评审的时候满满的认同感,但评审结束不久,就给我冠了个事B的称号。

今天我就把当时的这些话儿整理整理,让大家说道说道,我到底是不是个事B。淦!

一个任务处理例子

代码的结构大体是这样的。

通过定时,这段代码每天晚上凌晨都要对数据库的记录进行一遍对账。主要的逻辑,就是使用独立的线程,渐进式的读取数据库中的相关记录,然后把这些记录,放在循环中逐条进行处理。

ExecutorService service = Executors.newFixedThreadPool(10);
...
service.submit(()->{while(true){if(CollectionUtils.isEmpty(items)){break;}List<Data> items = queryPageData(start, end); // 分页逻辑for(Data item : items){try {Thread.sleep(10L);} catch (InterruptedException e) {//noop }processItem(item);}}
});

等一下。在代码马上被翻过去的时候,我叫停了,这里的processItem没有捕获异常

通常情况下,这不会有什么问题。但静好的岁月,总是偶尔会被一些随机的事故打断。如果这是你任务的完整代码,那它就有一种非常隐晦的故障处理方式。即使你的单元测试写的再好,这段代码我们依然可以通过远程投毒的方式,通过问题记录来让它产生问题。

是的。以上代码的根本原因,就是没有捕捉processItem函数可能产生的异常。如果在记录处理的时候,有任何一条抛出了异常,不管是checked异常还是unchecked异常,整个任务的执行都会终止!

不要觉得简单哦,踩过这个坑的同学,请记得扣个666。或者翻一下你的任务执行代码,看看是不是也有这个问题。

Java编译器在很多情况下都会提示你把异常给捕捉了,但总有些异常会逃出去,比如空指针异常。如下图,RuntimeException和Error都属于unchecked异常。

RuntimeException可以不用try...catch进行处理,但是如果一旦出现异常,则会导致程序中断执行,JVM将统一处理这些异常。

你捕捉不到它,它自然会让你的任务完蛋。

如果你想要异步的执行一些任务,最好多花一点功夫到异常设计上面。在这上面翻车的同学比比皆是,这辆车并不介意再带上你一个。

评审的小伙很谦虚,马上就现场修改了代码。

不要生吞异常

且看修改后的代码。

ExecutorService service = Executors.newFixedThreadPool(10);
...
service.submit(()->{while(true){if(CollectionUtils.isEmpty(items)){break;}List<Data> items = queryPageData(start, end); // 分页逻辑for(Data item : items){try {Thread.sleep(10L);} catch (InterruptedException e) {//noop }try{processItem(item);}catch(Exception ex){LOG.error(...,ex);}}}
});
...
service.shutdownNow();

为了控制任务执行的频率,sleep大法是个有效的方法。

代码里考虑的很周到,按照我们上述的方式捕捉了异常。同时,还很贴心的把sleep相关的异常也给捕捉了。这里不贴心也没办法,因为不补齐这部分代码的话,编译无法通过,我们姑且认为是开发人员的水平够屌。

由于sleep抛出的是InterruptedException,所以代码什么也没处理。这也是我们代码里常见的操作。不信打开你的项目,忽略InterruptedException的代码肯定多如牛毛。

此时,你去执行这段代码,虽然线程池使用了暴力的shutdownNow函数,但你的代码依然无法终止,它将一直run下去。因为你忽略了InterruptedException异常。

当然,我们可以在捕捉到InterruptedException的时候,终止循环。

try {Thread.sleep(10L);
} catch (InterruptedException e) {break;
}

虽然这样能够完成预期,但一般InterruptedException却不是这么处理的。正确的处理方式是这样的:

while (true) {Thread currentThread = Thread.currentThread();if(currentThread.isInterrupted()){break;}try {Thread.sleep(1L);} catch (InterruptedException e) {currentThread.interrupt();}
}

除了捕捉它,我们还要再次把interrupt状态给复位,否则它就随着捕捉给清除了。InterruptedException在很多场景非常的重要。当有些方法一直阻塞着线程,比如耗时的计算,会让整个线程卡在那里什么都干不了,InterruptedException可以中断任务的执行,是非常有用的。

但是对我们现在代码的逻辑来说,并没有什么影响。被评审的小伙伴不满意的说。

还有问题!

有没有影响是一回事,是不是好的习惯是另一回事 。我尽量的装了一下B,其实,你的异常处理代码里还有另外隐藏的问题。

还有什么问题?,大家都一改常日慵懒的表情,你倒是说说

我们来看一下小伙伴现场改的问题。他直接使用catch捕获了这里的异常,然后记录了相应的日志。我要说的问题是,这里的Exception粒度是不对的,太粗鲁。

try{processItem(item);
}catch(Exception ex){LOG.error(...,ex);
}

processItem函数抛出了IOException,同时也抛出了InterruptedException,但我们都一致对待为普通的Exception,这样就无法体现上层函数抛出异常的意图。

比如processItem函数抛出了一个TimeoutExcepiton,期望我们能够基于它做一些重试;或者抛出了SystemBusyExcption,期望我们能够多sleep一会,给服务器一点时间。这种粗粒度的异常一股脑的将它们捕捉,在新异常添加的时候根本无法发现这些代码,会发生风险。

一时间会议室里寂静无比。

我觉得你说的很对 ,一位比较资深的老鸟说, 你的意思是把所有的异常情况都分别捕捉,进行精细化处理。但最后你还是要使用Exception来捕捉RuntimeException,异常还是捕捉不到啊

果然是不同凡响的发问。

优秀的、标准的代码写法,其中无法实施的一个重要因素,就是项目中的其他代码根本不按规矩来。如果我们下层的代码,进行了正确的空指针判断、数组越界操作,或者使用类似guava的Preconditions这类API进行了前置的异常翻译,上面的这种问题根本不用回答。

但上面这种代码的情况,我们就需要手动的捕捉RuntimeException,进行单独的处理。

你们这个项目,烂代码太多了,所以不好改。我虽然有情商,但我更有脾气。

大家不欢而散。

End

我实在是想不通,代码review就是用来发现问题的。结果这review会一开下来,大家都在背后讽刺我。这到底是我的问题呢?还是这个团队的问题呢?让人搞不懂。

你们在纠结使用Integer还是int的时候,我也没说什么呀,现在就谈点异常处理的问题,就那么玻璃心受不了了。这B不能全都让你们装了啊。

什么?你要review一下我的代码?看看我到底有没有像我说的一样写代码,有没有以身作则?是在不好意思,我可是架构师哎,我已经很多年没写代码了。

你的这个愿望让你落空了!

往期推荐

轻量级动态线程池才是“王道”?

synchronized 底层了解一下...

如何抓到入侵网站的黑客?

有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

好文章,我在看❤️

代码review,瑞出事来了!相关推荐

  1. 代码review,瑞出事来了

    不久之前,部门进行了一次代码评审. 代码整体比较简单,该吹B的地方都已经吹过了,无非是些if else的老问题而已.当翻到一段定时任务的一步执行代码时,我的双眼一亮,觉得该BB两句了. 谁知这群家伙, ...

  2. 由学习《软件设计重构》所想到的代码review(二)

    前言 对于一个程序员来讲如何来最直接的来衡量他的技术能力和产出呢?我想最直观的作法是看他的代码编写能力,就拿我经常接触的一些程序员来看,他们买了很多技术重构类书籍,但是看完后代码编写能力并没有显著提高 ...

  3. 如何有效地进行代码 Review?

    研发都知道代码 Review 的重要性,在腾讯代码 Review 也越来越受大家重视,作为腾讯专有云平台研发的一员,我参与了大量的代码 Review,明显地感受到有效的代码 Review 不但能提高代 ...

  4. Gerrit代码Review高阶实战

    Gerrit代码Review高阶实战 Gerrit代码Review高阶实战 Gerrit 是一个免费.开放源代码的代码审查软件,使用网页界面.利用网页浏览器,同一团队的程序员,可以相互审阅彼此修改后的 ...

  5. Gerrit代码Review入门实战 1

    Gerrit代码Review入门实战 库 代码审核 Android Gerrit 经验分享 阅读23889  代码审核(Code Review)是软件研发质量保障机制中非常重要的一环,但在实际项目执行 ...

  6. 代码REVIEW的一般方法

    代码REVIEW的工作是软件工程中不能忽视的工作,是软件质量活动之一,对保障和改善软件工程质量有比较明显的作用,特别是对团队进行开发的软件工程.本文主要以C代码的视角描述CODE REVIEW的一般方 ...

  7. 研发团队如何借助Gitlab来做代码review

    代码review是代码质量保障的手段之一,同时开发成员之间代码review也是一种技术交流的方式,虽然会占用一些时间,但对团队而言,总体是个利大于弊的事情.如何借助现有工具在团队内部形成代码revie ...

  8. 如何进行代码REVIEW

    一.代码REVIEW的利与弊 软件工程中,最后都落实在代码的实现上,软件要运行的没有问题,除了软件架构和系统设计设计到位,代码实现也是至关重要的一部分,再好的设计如果软件代码实现有问题,软件也不能跑起 ...

  9. java code review 工具_两款好用的代码Review工具 | 学步园

    最近团队内部呼吁代码质量的声音越来越高,项目组引入了代码Review机制.上次我由于不了解主站URL替换的规则,险些造成客户端图片读取错误,而代码Review则有助于帮我们发现代码潜在的问题. 主讲的 ...

最新文章

  1. 《网页设计创意书》读后感
  2. 神经网络可解释性的另一种方法:积分梯度,解决梯度饱和缺陷
  3. 界面Hello world
  4. 基于python的网站_基于Python的网站爬虫应用研究
  5. QT的QHashIterator类的使用
  6. McAfee:2012年风险与合规展望
  7. java 重复提交_java解决重复提交表单
  8. PS教程第二十二课:羽化选区
  9. php 查找所有函数,PHP(方法 函数 循环 和 数组 查找)
  10. 输入两个整数n和m,从数列1,2,3,……n中随意取几个数,使其和等于m 转载
  11. 图论算法——无向图的深度优先搜索和广度优先搜索
  12. 学习Java,真的可以月薪过万嘛?真实个人经历告诉你,记录了平时学习的内容以及学习过程中最真实的感受(二)
  13. itools苹果录屏大师_iTools联手顺网 专注服务7000万网吧用户
  14. 《持续集成实践指南》第3章 Gitlab基本配置与使用
  15. 自己DIY一个mp3播放器
  16. linux下XMind创建运行图标
  17. 跳出横向的、孤立的牵绊,真正站在新的角度来看待Web3.0
  18. python怎么编写在线excel_超简单:用Python让Excel飞起来(零基础学python,用python实现办公自动化)...
  19. [附源码]JAVA+ssm基于Java的运动健身平台(程序+Lw)
  20. Andorid 拍照、从相册中选择图片兼容7.0uri

热门文章

  1. Java高级语法笔记-多个异常处理
  2. android最简单存储数据结构,什么是存储要在多个Android设备上共享的同步数据的最佳数据结构?...
  3. php 几秒后返回,php计算两个时间差并返回差多少天、时、分、秒
  4. linux nslookup 解析不到dns_涉及DNS的简单操作,只看这一篇就够了
  5. python识图找图_利用python进行识别相似图片(二)
  6. 【OPTEE开发】从TA到PTA的功能设计
  7. css毛玻璃效果白边_使用css模拟vista毛玻璃效果
  8. 数据结构之二叉树的逻辑结构和遍历
  9. JS 动态创建元素、删除元素、替换元素、修改元素
  10. C/C++:Windows编程—Hook IE浏览器实现URL拦截及更改(上)