可见性指修改后其他线程能否立即看到,源于jmm中线程工作内存与主内存不同步;原子性指操作是否不可分割,如count++非原子会导致丢失更新;volatile保可见性不保原子性。

直接说清楚:可见性讲的是“改了别人能不能马上看到”,原子性讲的是“操作会不会被中途打断”。这两个不是一回事,但经常一起出问题。
可见性:改了值,对方线程却读不到最新值
核心原因在于 Java 内存模型(JMM)——每个线程有自己的工作内存(比如 CPU 缓存),变量修改后不一定立刻刷回主内存;其他线程读的时候,可能还在读自己缓存里的旧值。
典型例子:
- 一个线程执行 running = false 想停止循环
- 另一个线程在 while(running) 里空转,却一直不退出
这不是代码写错了,而是因为 running 没加 volatile,修改没强制同步到主内存,对方线程看不到变化。
保证可见性的办法:
- 用 volatile 修饰共享变量(最轻量、常用)
- 进入 synchronized 块或调用 Lock 的方法(锁释放前会刷回主内存)
- 使用 java.util.concurrent 包下的线程安全类(内部已处理)
原子性:看着是一步,其实分多步,中间可能被插队
比如 count++,JVM 实际拆成三步:读 count → 加 1 → 写回。两个线程同时执行,就可能出现“都读到 0、都加成 1、都写回 1”,最终结果是 1 而不是 2 —— 这叫丢失更新。
注意:简单赋值如 int x = 5; 是原子的;但 x++、x = x + 1、y = x 都不是原子操作。
保证原子性的常见方式:
- AtomicInteger 等原子类(底层 CAS + volatile,适合计数类场景)
- synchronized 或 ReentrantLock(适合多步骤组合逻辑,比如“查余额→扣款→记日志”)
- LongAdder(高并发累加,比 AtomicInteger 更高效)
别混淆:volatile 能保可见性,但不能保原子性
比如 volatile int count = 0;,虽然每次读都能看到最新值,但 count++ 依然非原子——volatile 不阻止“读-改-写”被拆开执行。这点面试时一定要点明,很多人在这里踩坑。
一句话收尾:可见性解决“看不看得见”,原子性解决“动不动得了”,两者配合才能写出真正线程安全的代码。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











