Java并发编程的核心是协调多线程对共享变量的读写顺序、可见时机和执行边界;i++非原子导致数据错乱,应优先用AtomicInteger;volatile仅保证可见性与有序性,不保证原子性;线程状态单向流转,start()不可重复调用;JMM是默认前提,happens-before规则不可或缺。

Java并发编程不是“多开几个线程就完事”,而是**在共享内存模型下,有意识地协调多个线程对变量的读写顺序、可见时机和执行边界**。新手常以为只要用了 Thread 或 Runnable 就算会并发,结果一上线就出现数据错乱、偶发超时、CPU飙高——问题不在“会不会写线程”,而在“有没有守住临界区、有没有看清内存视图”。
为什么 i++ 在多线程里一定会出错?
这不是 Java 的 bug,是原子性缺失的必然结果:i++ 看似一条语句,实际被 JVM 拆成三步:getstatic i → iadd → putstatic i。两个线程同时读到 i = 5,各自加 1 后都写回 6,最终结果还是 6,而不是预期的 7。
- 别用
synchronized包裹所有i++—— 锁粒度太大会拖垮吞吐量 - 优先用
AtomicInteger.incrementAndGet():底层靠 CPU 的CAS指令实现无锁原子更新 - 如果必须用普通变量,且逻辑复杂(比如先判断再修改),才考虑
synchronized或ReentrantLock
volatile 能不能代替 synchronized?
不能。它只解决**可见性 + 有序性**,不解决**原子性**。典型误用场景:
public class Counter {
private volatile int count = 0;
public void increment() {
count++; // ❌ 仍非原子!volatile 不管这行怎么执行
}
}
-
volatile适合做状态标志:比如private volatile boolean running = true;,配合while(running)控制循环退出 - 它插入内存屏障,禁止编译器/CPU 对其读写重排序,但不会阻止其他线程在你读-改-写之间插一脚
- 一旦涉及“读取→计算→写入”这类复合操作,
volatile就失效,必须升级为锁或原子类
线程启动后为啥调两次 start() 就抛 IllegalThreadStateException?
因为 Java 线程状态是严格单向流转的:NEW → RUNNABLE → TERMINATED,中间不可逆。调用 start() 后,线程进入 RUNNABLE,再次调用就违反状态机规则。
立即学习“Java免费学习笔记(深入)”;
- 别用
thread.run()试图“重启”线程——这只是普通方法调用,不创建新线程,也不改变状态 - 需要重复执行逻辑?用循环 + 条件控制,或者扔进线程池(
ExecutorService)反复提交Runnable - 真正要复用的是线程资源,不是线程对象本身;
Thread实例是一次性的
最常被忽略的一点:**JMM(Java内存模型)不是可选知识,而是默认前提**。你写的每一行共享变量访问,都在和工作内存、主内存、缓存一致性打交道。不理解 happens-before 规则,就等于在没有地图的情况下调试分布式系统——现象诡异,修复随机,上线即回归。











