static代码块“天然”线程安全,因JVM规范强制类初始化由单一线程完成,其余线程阻塞等待;但该安全仅限初始化阶段,且不同类加载器加载的同一类会各自执行static块,互不影响。

Java 中 static 代码块本身是线程安全的,但这种安全性完全依赖于 JVM 对类初始化过程的规范约束,而非代码块自身加锁;其执行时机和可见性还与类加载器强相关——不同类加载器加载的同一类,static 块会各自执行且互不影响。
static 代码块为何“天然”线程安全
JVM 规范明确要求:一个类的初始化(包括执行 static 块和静态字段赋值)必须由单个线程完成,其他线程在该类处于“正在初始化”状态时会被阻塞,直到初始化完成。这意味着:
- 即使多个线程同时首次主动使用该类(如调用静态方法、访问静态字段、创建实例等),也只会有一个线程真正执行
static块,其余线程等待并复用初始化结果; - 无需手动同步,JVM 底层通过类对象的初始化锁(intrinsic lock on the Class object)保证了原子性和可见性;
- 这种安全仅限于“类初始化阶段”,不延伸到后续对静态变量的并发读写——若静态变量可变且被多线程修改,仍需额外同步。
类加载器是 static 块隔离的关键边界
同一个类名,若由不同的类加载器加载,JVM 将视为**完全不同的类**,各自拥有独立的类对象、静态变量和初始化状态:
- 每个类加载器加载该类时,都会独立触发一次类加载→链接→初始化流程,
static块也会分别执行一次; - 两个类加载器加载的
MyUtils类,它们的static int count = 0;是两份独立内存,互不可见; - 常见场景如 Web 容器(Tomcat)、OSGi、热部署框架中,父子类加载器或并行类加载器可能导致同一类被多次初始化,需警惕重复执行副作用(如注册监听器、初始化单例、写日志等)。
如何验证和规避常见陷阱
可通过日志或调试确认 static 块是否被意外多次执行:
立即学习“Java免费学习笔记(深入)”;
- 在
static块中打印当前类加载器:System.out.println(MyClass.class.getClassLoader());; - 避免在
static块中做有外部依赖或非幂等操作(如连接数据库、发 HTTP 请求、修改全局配置); - 若需跨类加载器共享状态,改用服务注册、SPI 机制或显式传入类加载器上下文,而非依赖静态变量;
- 对关键初始化逻辑,可结合
AtomicBoolean或双重检查加锁(虽通常不必要),增强可观察性和可控性。
不复杂但容易忽略:线程安全只保初始化过程,类加载器决定“类”的身份边界。










