java中static变量天生不线程安全,因其共享且jvm不保证原子性、可见性与有序性;应优先考虑消除共享(如threadlocal或实例变量),其次用synchronized或atomicinteger,volatile仅适用于简单状态标志。

Java 中的 static 变量天生不线程安全,因为它被所有线程共享,而 JVM 不保证对其读写操作的原子性、可见性与有序性。多个线程同时执行 count++ 这类操作时,极易出现结果偏小、数据丢失等不可重现的并发问题。
用 synchronized 控制临界区访问
这是最直接、最易理解的方式,适用于逻辑简单、竞争不激烈的场景。
- 修饰静态方法:自动以当前类的
Class对象为锁,例如public static synchronized void increment() { count++; } - 同步代码块:粒度更细,只锁真正修改变量的部分,推荐使用
synchronized (MyClass.class),避免误用实例对象导致锁失效 - 注意不要用
this或任意新建对象作锁,否则无法保证全局互斥
用 AtomicInteger 等原子类替代基础类型
对整数、布尔、引用等常见类型,优先选用 java.util.concurrent.atomic 包下的原子类,性能好且无锁。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 声明:
private static AtomicInteger count = new AtomicInteger(0); - 更新:
count.incrementAndGet()或count.addAndGet(1),底层基于 CAS,天然支持原子性和可见性 - 局限:仅适用于单变量的原子操作;若需多个变量协同更新(如余额+日志),仍需配合锁或事务机制
用 volatile 修饰 + 明确使用边界
volatile 能解决可见性和指令重排序问题,但不能保证复合操作的原子性,因此使用范围很窄。
- 适用:状态开关(
private static volatile boolean running = true;)、初始化完成标志、只写一次后只读的配置项 - 不适用:
count++、list.add()、任何含“读-改-写”三步的操作 - 可配合 double-checked locking 使用,但不能单独用于计数类逻辑
改用 ThreadLocal 或实例变量消除共享
如果变量本就不需要跨线程共享,强行用 static 反而是设计缺陷。
-
ThreadLocal:为每个线程提供独立副本,适合上下文信息(如用户 ID、格式化器、事务状态),彻底避开竞争 - 实例变量 + 容器管理:将静态变量改为普通成员变量,由 Spring 等框架管理 Bean 生命周期,通过依赖注入传递数据
- 关键判断:这个值是否真的必须被所有线程共同读写?多数时候答案是否定的
不复杂但容易忽略:很多线程安全问题其实源于过早引入全局状态。先问一句“它非得是 static 吗”,往往比加锁更治本。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










