类变量(static变量)默认线程非安全,因其被所有线程共享且可变时易引发竞态与可见性问题;实例变量安全性取决于对象是否共享:单例模式下等同于类变量,非单例且无共享引用时线程安全。

类变量(静态变量)和实例变量在多线程环境下的安全性,关键不在于“是什么”,而在于“是否被多个线程共享且可变”。只要满足“共享 + 可变 + 读写混用”,就存在线程安全风险。
类变量(static 变量)默认非线程安全
类变量属于类本身,存储在方法区,被所有实例和线程共享。即使只有一个线程执行写操作,其他线程的读也可能看到中间态或过期值。
- 例如 private static int counter = 0;,多个线程并发执行 counter++,结果大概率小于预期——因为 ++ 不是原子操作(读、加、写三步)
- 即使只读不写,若未用 final 或 volatile 修饰,仍可能因可见性问题读到旧值
- 若仅用于初始化后只读(如配置常量),且确保发布安全(如 static final),则线程安全
实例变量的安全性取决于对象生命周期
实例变量属于对象,分配在堆中。其线程安全性由“该对象是否被多个线程共享”决定:
- 单例对象的实例变量 ≈ 类变量:如 Spring 中的 singleton Bean,多个线程调用同一实例的方法并修改其字段,就会出现竞态条件
- 每次请求新建对象(prototype/局部 new):每个线程操作的是不同对象的副本,互不影响,天然线程安全
- 注意:若实例变量引用了外部共享对象(如传入的 List、Map),安全责任转移到那个被引用的对象上
真正决定安全的不是变量类型,而是访问模式
Java 并发规范强调:线程安全的本质是对“共享且可变状态”的协调。因此需关注实际运行时的数据流向:
- 只读访问(无论 static 还是实例)→ 安全(前提是发布正确)
- 有写操作 → 必须同步:可用 synchronized、ReentrantLock、原子类(AtomicInteger)、volatile(仅适用于简单状态标志)等手段保护临界区
- 避免将可变对象作为公共字段暴露(如 public ArrayList
list),这会无意中让调用方绕过封装直接并发修改











