应避免在jsp或遗留java web项目中滥用synchronized,如锁静态工具类、全局格式化器或在jsp脚本中加锁;正确做法是用局部实例、java.time api、细粒度锁、专用锁对象或线程安全替代方案。

在 JSP 或遗留 Java Web 项目中,synchronized 往往被“抄作业式”滥用——比如直接锁静态工具类、全局格式化器(如 SimpleDateFormat),或在 JSP 脚本片段里粗暴加锁。这类用法看似解决了线程安全问题,实则极易引发锁竞争、阻塞请求线程、拖垮吞吐量,甚至掩盖更根本的设计缺陷。避免滥用的关键不是“少用”,而是“用对地方、用对对象、用对粒度”。
别锁共享的静态工具对象
典型反例:public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");,然后在 JSP 中写 。
问题在于:这个 sdf 是全应用共享的单例,所有请求都争抢同一把锁,相当于把高并发请求串行化处理。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- ✅ 正确做法:每次使用都新建局部实例(JSP 中直接
new SimpleDateFormat(...))——创建开销极小,远低于锁竞争成本 - ✅ 更优做法:改用
java.timeAPI(如DateTimeFormatter),它是不可变且线程安全的,完全无需同步 - ⚠️ 注意:不要用
synchronized(C1.class)替代——这把锁范围更大,影响整个类的所有静态同步块
拒绝在 JSP 脚本片段里写 synchronized
JSP 中的 本质是生成 Servlet 的 _jspService() 方法体,其中的 synchronized 块实际锁的是当前 Servlet 实例(即 this)。而现代容器(如 Tomcat)通常复用同一个 Servlet 实例处理所有请求,结果就是所有请求被强制排队。
- ✅ 正确做法:把业务逻辑抽离到独立的 Service 类中,按需控制锁粒度;JSP 只负责展示,不碰共享状态
- ✅ 若必须同步某段计算,优先用无锁方式:例如用
AtomicInteger替代synchronized计数,用ConcurrentHashMap替代手动同步的HashMap - ❌ 避免写法:
或(request对象非线程安全但也不该作为锁对象)
用细粒度锁替代粗粒度方法锁
老项目常见模式:给整个工具方法加 synchronized,例如 public static synchronized String formatTime(Date d)。这等于让所有调用者为彼此等待。
- ✅ 改成同步代码块,只锁真正需要互斥的资源:比如只在访问某个共享缓存时加锁,而非整个格式化流程
- ✅ 用专用锁对象替代
this或Class:声明private static final Object timeLock = new Object();,再synchronized(timeLock)—— 至少语义清晰、范围可控 - ✅ 极端情况可考虑读写锁(
ReentrantReadWriteLock):若读多写少(如配置加载后只读),能显著提升并发度
优先用线程安全替代加锁
很多老项目加锁,其实只是因为用了非线程安全的老类(如 SimpleDateFormat、ArrayList、HashMap)。与其锁,不如换。
- ✅ 时间处理:全面迁移到
java.time(LocalDateTime、DateTimeFormatter) - ✅ 集合操作:读多场景用
CopyOnWriteArrayList或ConcurrentHashMap;写多且需强一致性,再评估是否真需锁 - ✅ 缓存类:用
Caffeine或Guava Cache,它们内部已做并发优化,无需外部同步
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










