本篇整理阿里巴巴 Java 开发手册中并发编程、异常处理和日志规约部分,这三块是日常开发中最容易出线上 bug 的区域。
一、并发编程
线程池不允许用 Executors 创建
手册明确禁止使用以下创建方式:
1 | // 禁止 |
原因:
newFixedThreadPool和newSingleThreadExecutor的等待队列是LinkedBlockingQueue,长度 Integer.MAX_VALUE,可能堆积大量请求导致 OOM。newCachedThreadPool允许创建的线程数是 Integer.MAX_VALUE,极端情况下线程数暴涨,OOM。
正确做法,手动构造 ThreadPoolExecutor:
1 | ThreadPoolExecutor executor = new ThreadPoolExecutor( |
线程要有名字
无名线程在日志和线程 dump 里显示为 Thread-0、Thread-1,出问题时完全无法定位。
SimpleDateFormat 线程不安全
1 | // 错误:SimpleDateFormat 有状态,多线程共享会产生日期错乱 |
加锁顺序要一致,避免死锁
多个线程需要同时获取多把锁时,必须保证所有线程以相同顺序加锁:
1 | // 两个方法都先锁 lockA 再锁 lockB——不会死锁 |
如果一个方法 A→B 加锁,另一个方法 B→A 加锁,就会产生死锁。
二、异常处理
不要捕获 Exception 大类
1 | // 错误:吞掉了所有异常,包括你不打算处理的 |
这么写的问题:
- 把业务异常、系统异常、NPE 全都一并吞掉
- 上层调用者收不到任何信号,无法做针对性处理
正确做法:
1 | try { |
不要用异常做流程控制
1 | // 错误:用异常控制流程,性能差且语义不清 |
finally 里不要有 return
1 | // 错误:finally 里的 return 会覆盖 try 里的 return,且会吞掉异常 |
自定义异常要加 cause
1 | // 错误:原始堆栈丢失,排查困难 |
三、日志规约
不要用 System.out.println
生产代码中任何 System.out 都不应该存在。原因:
- 输出到标准输出流,不受日志框架管理(无日志级别、无日志轮转)
- 频繁调用会阻塞 IO
占位符而不是字符串拼接
1 | // 错误:无论日志级别是否开启,字符串拼接都会执行 |
打印异常时用 e 参数
1 | // 错误:只打印了 message,没有堆栈信息 |
慎用 warn,不要泛滥
手册建议:warn 应该表示”需要人工关注但不影响主流程”的情况。如果 warn 日志太多,oncall 的人会失去敏感性,真正需要关注的信息被淹没。
小结
并发、异常、日志是线上 bug 的高发区,原因都是局部看起来能跑,全局才暴露问题:线程池在低负载下不出事,流量一上来 OOM;异常吞掉了但业务数据已经错了;日志不完整导致问题无法复现。养成规范习惯,比事后排查省力得多。



