单纯靠 synchronized 无法防范线程池爆满,反而可能因阻塞占用 tomcat 线程导致“卡死”;应减少阻塞、缩短临界区、改用 atomicinteger、concurrenthashmap、分段锁、stampedlock 或异步单线程消费,并配合 tomcat 线程池调优与 sentinel 限流及 jstack/jmx/prometheus 监控。

单纯靠 synchronized 无法防范线程池爆满,反而可能成为诱因——它会让线程在锁上阻塞等待,占用 Tomcat 工作线程却不释放,最终把线程池“卡死”。真正要做的,是减少阻塞、缩短临界区、用更轻量的协作方式替代粗粒度锁。
识别 synchronized 的高危用法
以下写法在高并发下极易拖垮线程池:
- 在 Controller 或 Service 方法上直接加
synchronized(等同于锁整个实例),所有请求串行执行 - 同步块内调用慢接口(如远程 HTTP、数据库写入、文件 IO),线程长时间挂起
- 锁对象是全局静态变量或长生命周期单例,导致跨请求竞争
- 嵌套多层 synchronized 或与 ReentrantLock 混用,增加死锁风险
用更合理的并发控制替代
不是不用同步,而是选对工具和粒度:
- 优先用无锁结构:高频读写计数器用
AtomicInteger,共享状态缓存用ConcurrentHashMap - 细粒度锁:按业务维度分段加锁(如按用户 ID 取模后锁定对应桶),避免全局互斥
- 读多写少场景用
StampedLock或ReadWriteLock,让并发读不阻塞 - 必须串行的逻辑,改用异步队列 + 单线程消费者处理(如用
Executors.newSingleThreadExecutor()),不占 Tomcat 线程
配合 Tomcat 线程池做主动防护
即使代码优化了,也要防止异常流量冲垮系统:
- 设合理 maxThreads:8 核服务器建议 200–400,避免盲目设 1000+ 加剧上下文切换
- acceptCount 显式限制:设为 100–200,超出直接拒绝(返回 503),不堆积连接
- connectionTimeout 设为 15–20 秒:及时清理空闲或卡死的连接
- 搭配 Sentinel 或 Resilience4j 做接口级限流:在进入 synchronized 逻辑前就熔断,不让压力传导到底层
监控与快速定位
上线后必须能第一时间发现锁瓶颈:
- 用
jstack -l <pid></pid>抓线程快照,搜索java.lang.Thread.State: BLOCKED和锁对象哈希值 - 通过 JMX 查看
Catalina:type=ThreadPool,name="tomcatThreadPool"的currentThreadsBusy和queueSize - 在关键 synchronized 块前后打日志(含耗时),观察是否出现持续 >1s 的执行延迟
- 接入 Prometheus + Grafana,监控线程池利用率突增与 GC 频次联动关系
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











