跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何在Java项目中制定合理的并发编程规范_阿里巴巴Java开发手册中多线程规约的落地实践

ExecutorService 必须通过Executors工厂方法或显式配置ThreadPoolExecutor创建,因手动new易触发OOM或任务堆积,根本原因是默认无界队列和AbortPolicy拒绝策略导致;实操需明确队列容量、拒绝策略、线程命名及业务前缀,并在Spring中用@Bean声明配合@PreDestroy。 为什么
ExecutorService
不能直接用
new ThreadPoolExecutor(...)
手动构造 手册里反复强调“线程池必须通过
Executors
工厂方法创建”,但实际落地时很多人图省事直接 new,结果在线上压测或流量突增时频繁触发 OOM 或任务堆积。根本原因不是语法错,而是默认参数陷阱:
Executors.newFixedThreadPool()
底层用的是无界
LinkedBlockingQueue
,任务持续提交却消费不及时,队列无限膨胀;而手动 new
ThreadPoolExecutor
时若忽略
RejectedExecutionHandler
,默认的
AbortPolicy
会直接抛
RejectedExecutionException
,业务没兜底就挂了。 实操建议: 强制使用
new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit, new ArrayBlockingQueue(queueCapacity), r -> new Thread(r, "biz-task-" + counter.getAndIncrement()), new ThreadPoolExecutor.CallerRunsPolicy())
—— 明确队列容量、拒绝策略、线程命名 所有线程池变量名带业务前缀,如
orderNotifyExecutor
,禁止泛用
executor
在 Spring 环境中统一用
@Bean
声明,配合
@PreDestroy
调用
shutdown()
,避免应用停机时线程残留
synchronized
和
ReentrantLock
到底该选哪个 不是“新代码一律用
ReentrantLock
”,也不是“老代码全留着
synchronized
”。关键看是否需要可中断、超时获取、公平锁或多个条件变量。日常临界资源保护(比如缓存更新、计数器累加),
synchronized
更轻量、JVM 优化成熟、不易漏写释放逻辑;而涉及复杂协作(如生产者消费者需分别等待不同条件),
ReentrantLock
的
newCondition()
才能解耦。 常见错误现象: 立即学习 “ Java免费学习笔记(深入) ”; 用
ReentrantLock
却忘记在
finally
块调用
unlock()
,导致死锁 在持有
synchronized
时调用外部服务(如 HTTP 请求),阻塞整个锁粒度,吞吐骤降 误以为
ReentrantLock
性能一定优于
synchronized
—— 在无竞争场景下,后者通常更快 为什么
ThreadLocal
不清理就会内存泄漏 根本不在“线程没销毁”,而在
ThreadLocalMap
的 key 是弱引用,value 是强引用。当
ThreadLocal
变量被回收后,key 变为
null
,但 value 仍挂在 map 里,且该 map 隶属于线程对象,只要线程(尤其是线程池里的常驻线程)不死,value 就一直占着堆内存。线上最典型的表现是:服务运行几天后 Full GC 频繁,dump 发现大量业务对象无法回收。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 实操建议: 所有
ThreadLocal
必须配对调用
remove()
,尤其在
try-finally
中,不能只依赖
set(null)
禁止在工具类里定义静态
ThreadLocal
存储用户上下文,应改用方法参数透传或
InheritableThreadLocal
(仅限父子线程传递) Spring 环境下优先用
RequestContextHolder
,它内部已封装了
remove()
逻辑 CompletableFuture 异步链中异常到底怎么捕获 用
thenApply
或
thenAccept
处理结果,但上游抛了异常,下游根本收不到——因为异常不会穿透到这些方法,而是静默吞掉,最终整个链在某个环节卡住或返回 null。真正起作用的是
exceptionally()
、
handle()
或
whenComplete()
,但三者语义不同:前者只处理异常,后两者都执行但
handle()
允许你返回新值,
whenComplete()
只做副作用(如打日志、发告警)。 容易踩的坑: 在
thenCompose
返回的新
CompletableFuture
上漏加异常处理器,导致嵌套异常丢失 用
join()
阻塞获取结果,却没包 try-catch,把
CompletionException
直接抛给容器(如 Tomcat),触发 500 多个异步分支用
allOf()
汇聚,但没逐个检查每个 future 的
isCompletedExceptionally()
,误判整体成功 复杂点在于:异常类型可能被包装多层,
getCause()
得连剥两层才能拿到原始业务异常。这点很容易被忽略,结果告警里全是
CompletionException
,看不出真实问题。

相关文章