静态代码块本身线程安全,jvm保证其仅执行一次;但若用它初始化非线程安全的共享变量(如hashmap),后续并发读写仍会导致线程安全问题。

静态代码块本身不会引发多线程加载竞争,但用它初始化全局缓存变量时,若后续操作涉及读写共享状态,就可能暴露线程安全问题。关键不在“块是否安全”,而在于“块之后的缓存如何被访问”。
静态块执行是线程安全的
类加载由 JVM 保证单次、原子性完成。多个线程首次触发该类时,JVM 会通过内部锁(如 ClassLoader.loadClass 的同步机制)确保静态块仅执行一次。即使千个线程同时加载同一类,静态块也只运行一遍。
例如:
static {cache = new HashMap(); // 安全:仅执行一次
System.out.println("Cache initialized");
}
但普通 HashMap 缓存本身不线程安全
静态块只是完成了缓存容器的创建,如果后续用 get/put 直接操作这个 HashMap,就会出问题:
- 多个线程同时调用 put 可能导致链表成环(JDK 7)或数据丢失(JDK 8+)
- get 虽不修改结构,但在并发 put 下可能读到过期值或抛 ConcurrentModificationException
- 即使静态块里用了 synchronized 初始化,也无法保护后续所有读写操作
推荐实战组合:静态块 + 线程安全容器
真正稳妥的做法,是让静态块完成“一次性初始化”,而把线程安全交给底层容器保障:
- 用 ConcurrentHashMap 替代 HashMap:无需额外加锁,读操作无锁,写操作分段/乐观控制,适合高并发缓存场景
- 静态块中直接赋值,简洁可靠:
static {
cache = new ConcurrentHashMap();
} - 若需延迟初始化(比如依赖外部配置),可用 Holder 模式 或 Double-Checked Locking,但对缓存变量通常没必要
不建议的误区
以下方式看似“加了锁就安全”,实则掩盖本质问题,且易引入死锁或性能瓶颈:
- 在静态块里用 synchronized 包裹 new HashMap()——多余,因为加载本就是串行的
- 后续所有 get/put 都用同一个全局锁(如 synchronized(GlobalCache.class))——严重串行化,吞吐骤降
- 混用 static final 和非线程安全集合——final 只保证引用不变,不保证其内部状态安全











