Java 并发工具很多,ThreadLocal、AQS、锁、信号量、限流器、异步 I/O 都能解决问题,也都可能制造新问题。使用它们时,最重要的是先分清隔离、同步、限流和异步分别解决什么。
ThreadLocal 是线程隔离,不是自动清理
ThreadLocal 给每个线程一份独立变量,常用于请求上下文、用户信息、traceId、格式化对象等。
它的风险在线程池场景里最明显。线程不会随请求结束而销毁,如果请求结束后没有调用 remove(),旧值会留在线程的 ThreadLocalMap 里,后续请求复用同一线程时可能读到脏数据,也可能造成内存泄漏。
使用建议很简单:谁设置,谁清理。最稳妥的写法是在 try finally 里 remove()。
AQS 是很多同步器的底座
AQS 维护一个 state 和一个等待队列,很多并发工具都基于它实现,例如 ReentrantLock、Semaphore、CountDownLatch。
独占模式适合一次只允许一个线程执行,例如可重入锁;共享模式适合多个线程同时通过,例如信号量和倒计时门闩。
理解 AQS 的价值不在于每次都去实现同步器,而是在排查锁等待时知道:线程获取资源失败后会进入队列,释放资源时再唤醒后续线程。阻塞不是凭空发生的,它有队列、有状态、有唤醒路径。
限流和并发控制不是一回事
RateLimiter 控制单位时间内允许通过多少请求,关注的是速率。例如每秒 100 个请求。
Semaphore 控制同一时刻最多有多少任务在执行,关注的是并发度。例如最多 4 个上传任务同时跑。
两者经常被混用。抢购接口可能需要限流,避免瞬间请求打满系统;文件上传或批处理更可能需要并发度控制,避免同时占用太多 CPU、内存或连接。
先问清楚要限制的是“每秒多少次”,还是“同时多少个”。
异步 I/O 不是没有成本
异步 I/O 让调用方发起操作后不必一直阻塞等待,CPU 可以继续做别的事。数据会进入内核缓冲区,由操作系统和设备继续处理,完成后再通知应用。
它适合大量 I/O 等待场景,但不意味着业务复杂度消失。异步代码要处理回调、超时、取消、资源释放和错误传播。排障时也要能串起异步链路,否则问题会比同步代码更难定位。
进程和线程排障要看真实状态
线上看到同一个进程下有多个线程、不同状态和不同等待点,不要只看进程名。要结合 PID、线程 ID、状态、CPU 使用、堆栈和父子关系判断。
常见步骤是:
- 先用
top、ps或监控定位高 CPU 或阻塞线程。 - 再用
jstack、线程 dump 或 profiler 看线程栈。 - 对线程池问题,结合队列长度、活跃线程数、拒绝次数和任务耗时。
并发排障不能只看一行日志,要把线程状态和业务调用链对上。
实用结论
Java 并发工具要按问题类型选:线程隔离用 ThreadLocal,互斥用锁,资源并发度用 Semaphore,调用速率用 RateLimiter,等待多个任务用 CountDownLatch 或类似工具。
工具本身不难,难的是生命周期和边界。是否清理、是否释放、是否超时、是否可取消、是否能观测,决定了并发代码能不能长期稳定运行。




