应优先使用细粒度锁、并发工具类和ThreadLocal,避免方法级同步、同步块内调用外部方法及ThreadLocal内存泄漏。

用 synchronized 保护共享状态,但别锁整个方法
直接在方法上加 synchronized 很方便,但容易锁粒度太大——比如一个类有 10 个字段,只改其中 1 个,却让所有线程排队等这一个方法,性能掉得明显。更稳妥的做法是锁具体对象或代码块:
private final Object lock = new Object();
public void updateCounter() {
synchronized (lock) {
count++;
}
}注意:锁对象必须是 final 且不对外暴露,否则别人也能 synchronized 它,导致意外串行或死锁。
优先用 java.util.concurrent 包里的线程安全类型
手写同步逻辑容易漏边界、错顺序。能用现成的就别自己造:
-
ConcurrentHashMap替代HashMap+ 手动同步 -
AtomicInteger替代int+synchronized自增 -
CopyOnWriteArrayList适合读多写少、迭代中可能修改的场景
ConcurrentHashMap 的 size() 不保证实时准确;AtomicInteger 的 getAndIncrement() 是原子的,但 ++ 运算符不是。
避免在同步块里调用外部可变对象的方法
这是隐蔽的死锁高发区。比如你在 synchronized(lockA) 里调用了某个第三方服务的 doSomething(),而它内部又去获取了 lockB ——如果另一个线程正拿着 lockB 并试图拿 lockA,就卡住了。
- 同步块内只做确定可控的操作:读写本类字段、调用纯函数(无副作用、不加锁)
- 把外部调用(DB 查询、HTTP 请求、回调)移出同步块
- 如必须组合操作,考虑用
ReentrantLock.tryLock(timeout, unit)设超时,避免无限等待
用 ThreadLocal 隔离线程间数据,但记得 remove()
当每个线程需要自己的副本(比如数据库连接、用户上下文),ThreadLocal 比共享变量+同步更轻量:
private static final ThreadLocal但 Web 应用中线程常被池化复用,不清理会导致内存泄漏——尤其DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
ThreadLocal 值是大对象或持有 ClassLoader 时。
- 每次用完显式调用
DATE_FORMAT.remove() - 在 Filter 或拦截器的
finally块里清理 - 不要依赖线程结束自动回收,JVM 不保证及时触发










