不能直接用普通HashMap在多线程环境下初始化共享变量——静态块初始化安全,但HashMap并发读写不安全;应改用ConcurrentHashMap或不可变集合等线程安全方案。

不能直接用普通 HashMap 在多线程环境下初始化共享变量——即使静态块只执行一次,后续并发读写仍会出问题。
静态块初始化本身安全,但容器不安全
Java 类加载机制保证静态代码块只执行一次,这是 JVM 层面的线程安全行为。例如:
这段没问题:static { cache = new HashMap
它确保 cache 引用被赋值一次,不会重复创建。
但问题出在后面:
一旦多个线程调用 cache.put(k, v) 或 cache.get(k),就可能触发:
- 链表成环(JDK 7)或数据丢失(JDK 8+)
- 迭代时抛 ConcurrentModificationException
- 读到过期值或结构不一致
正确做法:静态块 + 线程安全容器
把“一次性初始化”和“并发访问安全”分开处理,推荐组合:
- 用
ConcurrentHashMap替代HashMap,它内部已做细粒度同步或 CAS 控制 - 静态块中直接初始化,简洁可靠:
static { cache = new ConcurrentHashMap(); } - 若需延迟加载(比如依赖配置),可用 Holder 模式,但对缓存类变量通常没必要
避免常见误区
以下方式看似“加了锁就稳”,实则掩盖风险或损害性能:
- 在静态块里
synchronized包裹new HashMap()—— 多余,类加载本就是串行的 - 后续所有操作都用
synchronized(GlobalClass.class)—— 全局串行化,吞吐暴跌 - 声明为
static final HashMap——final只锁住引用,不保护内部状态
补充:其他安全初始化方式
如果场景更复杂,还可考虑:
-
static final Map—— 适合只读全局配置IMMUTABLE_CACHE = Collections.unmodifiableMap(...) -
private static volatile Map+ Double-Checked Locking(慎用,易出错)cache; - 用
AtomicReference管理可替换的缓存实例
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











