《Java 100 天进阶之路》第60.3篇:多线程生产环境避坑清单(2026版)
第60.3篇:多线程生产环境避坑清单(2026版)
📌系列导航:《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第60.2篇:多线程面试官追问陷阱合集 |
➡️ 下一篇:第60.4篇:多线程代码示例集
🗺️ 本文阅读地图(2 分钟速览)
多线程开发中,99% 的线上问题都集中在少数几个坑点上。本文汇总 20+ 个生产环境真实踩坑案例,每条配错误案例 + 正确做法,直接落地到代码规范:
| 分类 | 坑点数量 |
|---|---|
| 线程池陷阱 | 5 条 |
| 锁与并发工具陷阱 | 4 条 |
| 异步编程陷阱 | 3 条 |
| 原子类与 CAS 陷阱 | 3 条 |
| ThreadLocal 陷阱 | 4 条 |
| 综合排查陷阱 | 3 条 |
文章目录
- 第60.3篇:多线程生产环境避坑清单(2026版)
- 🗺️ 本文阅读地图(2 分钟速览)
- 一、线程池陷阱(5条)
- ❌ 坑1:Executors 创建线程池
- ❌ 坑2:线程池未设置线程名
- ❌ 坑3:submit 异常被吞没
- ❌ 坑4:线程池不关闭
- ❌ 坑5:IO 密集型线程数设置过大/过小
- 二、锁与并发工具陷阱(4条)
- ❌ 坑6:ReentrantLock 不 finally unlock
- ❌ 坑7:synchronized 锁对象用 String 常量
- ❌ 坑8:CountDownLatch 计数器不归零
- ❌ 坑9:死锁只用 jstack 不改代码
- 三、异步编程陷阱(3条)
- ❌ 坑10:CompletableFuture 不设超时
- ❌ 坑11:CompletableFuture 异常不处理
- ❌ 坑12:用 commonPool 做业务异步
- 四、原子类与 CAS 陷阱(3条)
- ❌ 坑13:volatile 做计数器
- ❌ 坑14:ABA 问题
- ❌ 坑15:LongAdder 做精确计算
- 五、ThreadLocal 陷阱(4条)
- ❌ 坑16:ThreadLocal 用完不 remove
- ❌ 坑17:ThreadLocal 存大对象
- ❌ 坑18:线程池用 InheritableThreadLocal
- ❌ 坑19:set(null) 替代 remove
- 六、综合排查陷阱(3条)
- ❌ 坑20:jstack 抓不到间歇性死锁
- ❌ 坑21:数据库连接池死锁
- ❌ 坑22:多线程共用 HashMap
- 📊 你的学习进度
- 👉 下一篇文章预告
一、线程池陷阱(5条)
❌ 坑1:Executors 创建线程池
// ❌ 错误ExecutorServicepool=Executors.newFixedThreadPool(10);// 无界队列 → 任务堆积 → OOM// ✅ 正确:手动创建有界线程池ExecutorServicepool=newThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,newArrayBlockingQueue<>(100),newThreadFactoryBuilder().setNameFormat("biz-%d").build(),newThreadPoolExecutor.CallerRunsPolicy());❌ 坑2:线程池未设置线程名
// ❌ 错误:线程名默认 pool-1-thread-1,无法定位业务ExecutorServicepool=Executors.newFixedThreadPool(10);// ✅ 正确:自定义 ThreadFactoryThreadFactoryfactory=r->newThread(r,"order-pool-"+System.currentTimeMillis());❌ 坑3:submit 异常被吞没
// ❌ 错误:submit 不处理 Future,异常静默消失pool.submit(()->{thrownewRuntimeException();});// ✅ 正确:处理 Future 或使用 executeFuture<?>future=pool.submit(task);try{future.get();}catch(ExecutionExceptione){log.error("",e);}// 或使用 execute(异常直接抛出)pool.execute(task);❌ 坑4:线程池不关闭
// ❌ 错误:程序退出时线程池未关闭,资源泄漏ExecutorServicepool=Executors.newFixedThreadPool(10);pool.submit(task);// ✅ 正确:优雅关闭try{pool.shutdown();if(!pool.awaitTermination(60,TimeUnit.SECONDS))pool.shutdownNow();}catch(InterruptedExceptione){pool.shutdownNow();}❌ 坑5:IO 密集型线程数设置过大/过小
// ❌ 错误:未区分 CPU/IO 类型newThreadPoolExecutor(4,4,...);// 假设 CPU 4 核,IO 密集型配置不足// ✅ 正确:IO 密集型 = CPU 核数 × 2 ~ 4intioCore=Runtime.getRuntime().availableProcessors()*2;newThreadPoolExecutor(ioCore,ioCore*2,...);二、锁与并发工具陷阱(4条)
❌ 坑6:ReentrantLock 不 finally unlock
// ❌ 错误:异常时锁不释放lock.lock();// 业务逻辑抛异常 → 死锁lock.unlock();// ✅ 正确:finally 释放lock.lock();try{/* 业务 */}finally{lock.unlock();}❌ 坑7:synchronized 锁对象用 String 常量
// ❌ 错误:不同模块共享同一字符串常量池StringLOCK="LOCK";synchronized(LOCK){/* 其他模块也用 "LOCK" 意外死锁 */}// ✅ 正确:使用专用 ObjectObjectlock=newObject();synchronized(lock){/* 只锁当前模块 */}❌ 坑8:CountDownLatch 计数器不归零
// ❌ 错误:异常时 countDown 未执行 → 主线程永久阻塞CountDownLatchlatch=newCountDownLatch(10);for(inti=0;i<10;i++){pool.submit(()->{try{/* 业务 */}finally{latch.countDown();}});}latch.await();// 某个任务异常退出 → 永久阻塞// ✅ 正确:await 带超时if(!latch.await(10,TimeUnit.SECONDS)){log.error("超时");}❌ 坑9:死锁只用 jstack 不改代码
// ❌ 错误:只发现死锁不分析根因// 输出 Found one Java-level deadlock,改完又出现// ✅ 正确:分析锁顺序,统一获取顺序或改用 tryLock// 方案:所有线程按固定顺序获取锁(先 lockA 再 lockB)三、异步编程陷阱(3条)
❌ 坑10:CompletableFuture 不设超时
// ❌ 错误:依赖服务卡死 → 线程永远阻塞CompletableFuture.supplyAsync(()->callSlowApi()).join();// ✅ 正确:设超时 + 兜底CompletableFuture.supplyAsync(()->callSlowApi()).completeOnTimeout("fallback",3,TimeUnit.SECONDS).join();❌ 坑11:CompletableFuture 异常不处理
// ❌ 错误:异常静默消失,排查极难CompletableFuture.supplyAsync(()->{thrownewRuntimeException();}).thenAccept(System.out::println);// 异常被吞// ✅ 正确:异常处理 + 日志CompletableFuture.supplyAsync(()->{thrownewRuntimeException();}).exceptionally(e->{log.error("",e);returnnull;}).thenAccept(System.out::println);❌ 坑12:用 commonPool 做业务异步
// ❌ 错误:业务异步用默认线程池CompletableFuture.supplyAsync(()->callApi());// 所有模块共用 commonPool → 互相影响// ✅ 正确:自建业务线程池CompletableFuture.supplyAsync(()->callApi(),AsyncPool.IO_POOL);四、原子类与 CAS 陷阱(3条)
❌ 坑13:volatile 做计数器
// ❌ 错误:多线程 count++,结果小于预期volatileintcount=0;// 100 线程各加 100 次,结果 < 10000// ✅ 正确:用 AtomicInteger 或 LongAdderAtomicIntegercount=newAtomicInteger(0);count.incrementAndGet();❌ 坑14:ABA 问题
// ❌ 错误:无锁栈/队列直接用 AtomicReferenceAtomicReference<Node>stack=newAtomicReference<>();// 可能发生 ABA → 链表断裂// ✅ 正确:用 AtomicStampedReferenceAtomicStampedReference<Node>stack=newAtomicStampedReference<>(null,0);❌ 坑15:LongAdder 做精确计算
// ❌ 错误:用 LongAdder 做精确统计LongAdderadder=newLongAdder();longtotal=adder.sum();// 弱一致性,累加过程有并发修改// ✅ 正确:需要精确值用 AtomicLong 或加锁AtomicLongcounter=newAtomicLong(0);longexact=counter.get();// 强一致性五、ThreadLocal 陷阱(4条)
❌ 坑16:ThreadLocal 用完不 remove
// ❌ 错误:线程池场景忘记清理,脏数据 + 内存泄漏ThreadLocal<String>context=newThreadLocal<>();context.set(userId);// 任务结束不 remove → 下一个请求读到旧 userId// ✅ 正确:try-finally 保证 removetry{context.set(userId);// 业务}finally{context.remove();// 必须!}❌ 坑17:ThreadLocal 存大对象
// ❌ 错误:存大对象导致单线程内存占用高ThreadLocal<byte[]>cache=newThreadLocal<>();cache.set(newbyte[1024*1024]);// 每个线程 1MB// 线程池 100 线程 → 100MB 内存泄漏// ✅ 正确:只存轻量级 key,大对象放 Redis/缓存ThreadLocal<String>cacheKey=newThreadLocal<>();Stringkey="user:"+userId;// 大对象从外部缓存获取❌ 坑18:线程池用 InheritableThreadLocal
// ❌ 错误:InheritableThreadLocal 不适用于线程池InheritableThreadLocal<String>tl=newInheritableThreadLocal<>();tl.set("parent");pool.submit(()->System.out.println(tl.get()));// 可能为 null// ✅ 正确:用 TransmittableThreadLocal(阿里开源)TransmittableThreadLocal<String>ttl=newTransmittableThreadLocal<>();ttl.set("parent");Runnabletask=TtlRunnable.get(()->System.out.println(ttl.get()));pool.submit(task);❌ 坑19:set(null) 替代 remove
// ❌ 错误:set(null) 不删除 Entry,内存泄漏仍然存在context.set(null);// Entry 还在,只是 value 为 null// ✅ 正确:必须 remove()context.remove();// 删除整个 Entry六、综合排查陷阱(3条)
❌ 坑20:jstack 抓不到间歇性死锁
// ❌ 错误:抓一次没看到 deadlock 就认为没问题// 间歇性死锁只在特定时序下触发// ✅ 正确:多次抓取 + 开启 JVM 参数// -XX:+PrintConcurrentLocks 打印锁信息// 或使用 Arthas thread -b 实时检测❌ 坑21:数据库连接池死锁
// ❌ 错误:所有连接被占满,新请求等待 → 但等待线程持有其他资源// 连接池大小 < 并发请求数 → 死锁// ✅ 正确:设置合理的连接池大小 + 超时// 每个请求的 SQL 执行时间 < 连接超时// 监控连接池使用率,提前扩容❌ 坑22:多线程共用 HashMap
// ❌ 错误:多线程直接写 HashMapMap<String,String>map=newHashMap<>();map.put("key","value");// 并发 → 死循环/数据丢失// ✅ 正确:用 ConcurrentHashMapMap<String,String>map=newConcurrentHashMap<>();map.put("key","value");📊 你的学习进度
- 当前:第60.3篇 ·多线程专题外挂:生产环境避坑清单
- ✅ 已完成:基础篇44篇 + 第45~60篇 + 第60.1~60.3篇
- 📖 正在学:第60.3篇
- ⏳ 待学习:第60.4篇(代码示例集)+ 第61~108篇
👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇
👉 下一篇文章预告
🚀下一篇:《第60.4篇:多线程代码示例集》
内容简介:面试可直接背诵的多线程代码模板,覆盖线程创建、锁使用、线程池、异步编排、CAS 等所有场景!
📌《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!
