降低并发风险的关键是让并发更可控可预测,需减少共享、明确边界、收窄临界区,并用现代工具替代易错模式:收缩共享状态、采用结构化并发、审慎使用ThreadLocal、下沉锁粒度及推进无锁化。

重构多线程代码时,降低并发风险的关键不是“去掉线程”,而是让并发行为更可控、更可预测。重点在于减少共享、明确边界、收窄临界区,并用现代工具替代易错模式。
收缩共享状态范围
多数并发问题源于不必要的共享。重构时优先问:这个变量真的需要被多个线程看到吗?
- 把方法内临时计算结果、中间对象声明为局部变量,天然线程安全
- 将类成员变量改为方法参数传入,或通过构造函数注入不可变配置(如 final 字段)
- 用 ConcurrentHashMap 替代 HashMap + 手动同步;用 LongAdder 替代 volatile int 或 synchronized 计数
用结构化并发替代裸线程管理
Java 24 的 StructuredTaskScope 是重构老式多线程代码的利器,它把“启动线程→等待→异常处理→清理”打包成一个作用域,避免漏掉 join 或未捕获异常导致的线程泄漏和静默失败。
- 把原来用
new Thread(() -> {...}).start()启动的异步任务,改写为scope.fork(() -> fetchUser()) - 用
scope.join()统一等待,不用手动调用多个thread.join() - 用
scope.throwIfFailed()集中处理任一子任务抛出的异常,不再需要分散的 try-catch
替换 ThreadLocal 并防范内存泄漏
ThreadLocal 在线程池场景下极易因忘记 remove() 导致内存泄漏。重构时应评估是否真需线程隔离:
立即学习“Java免费学习笔记(深入)”;
- 若只为传递上下文(如 traceId、用户身份),改用 TransmittableThreadLocal(TTL),它支持在线程池中自动复制和清理
- 若仅用于避免参数层层传递,考虑将上下文作为显式参数传入关键方法,提升可测性与可读性
- 彻底移除无 clear() 调用的 ThreadLocal 使用,尤其在 Filter、Interceptor 等生命周期长的组件中
锁粒度下沉与无锁化演进
重构同步块时,不只看“有没有锁”,更要看“锁了什么、锁多久、谁在等”:
- 把
synchronized(this)改为synchronized(lockObject),避免锁住整个实例影响无关方法 - 将大段逻辑拆解,只对真正修改共享状态的几行加锁,其余计算移到临界区外
- 对计数、标志位等简单操作,用 AtomicInteger、AtomicBoolean 替代 synchronized 块
- 高竞争场景下,考虑 StampedLock 的乐观读,避免读多写少时的写饥饿


















