java基本数据类型单次读写操作原子,但long/double在32位jvm上例外;复合操作如i++非原子,需atomicinteger、synchronized等机制保障线程安全。

Java 中基本类型和包装类本身不自带线程安全机制,它们的普通读写操作在多线程下是否原子,取决于操作类型和数据宽度;而要真正保障“复合操作”的原子性(比如自增、条件更新),必须借助额外手段。
基本类型单次读写天然原子,但 long/double 例外
Java 规范保证:对 int、boolean、byte、short、char 的单次读或单次写是原子的。例如:
-
int count = 10;—— 赋值动作原子 -
flag = false;—— 写操作原子
但 long 和 double 在 32 位 JVM 上可能被拆成两次 32 位操作,存在“半个值被改、半个没改”的风险。解决办法有两个:
- 用
volatile修饰(强制一次读/写,且保证可见性) - 直接使用
AtomicLong或AtomicDouble
包装类默认不保证原子性,不能直接用于并发计数
像 Integer、Boolean 这些包装类对象本身是不可变的,每次“修改”其实是新建对象(如 i++ → i = new Integer(i.intValue() + 1)),整个过程包含读取、计算、赋值三步,明显非原子。
常见误区:static Integer counter = 0; 然后多个线程调用 counter++ —— 结果大概率小于预期,因为竞态条件未受控。
真正保障原子性的推荐方式
不要依赖基本类型或包装类自身,而是选用以下任一可靠方案:
-
AtomicInteger / AtomicLong 等原子类:适合单变量简单运算(如计数、开关状态)。底层用 CAS + volatile,无锁、高效。例:
atomicCounter.incrementAndGet() - synchronized 同步块:适用于多步逻辑组合(如“余额 > 0 才扣款”),把读-判-写包进同一临界区。注意锁对象要统一、粒度别过大。
- ReentrantLock 配合 lock()/unlock():比 synchronized 更灵活(可中断、可超时、支持公平策略),但需手动确保 finally 中释放。
-
AtomicReference + 自定义对象:若需原子更新整个对象(如封装了多个字段的状态类),可用
AtomicReference<mystate></mystate>,配合compareAndSet实现乐观更新。
为什么 volatile 不够用?
volatile 只能保证单次读/写的可见性和禁止重排序,无法保障复合操作的原子性。例如:
volatile int v = 0;-
v++;—— 仍是读、加、写三步,中间可能被其他线程插入
所以 volatile 适合做状态标志(如 running = false),不适合做计数器或条件变更。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











