Java并发编程必须夯实基础:先掌握线程生命周期、synchronized与JMM,再学JUC;否则无法定位90%的并发bug。

ReentrantLock,否则连“为什么加锁后还出问题”都查不出。
从 Thread 和 Runnable 开始,别一上来就学 JUC
很多初学者直接啃 ConcurrentHashMap 或 ThreadPoolExecutor,结果连 Thread.start() 和 run() 的区别都说不清。真实场景中,90% 的并发 bug 都出在基础线程控制上。
-
Thread是执行单元,Runnable是任务契约——必须用new Thread(runnable).start()启动,直接调run()就是普通方法调用,不走新线程 - 线程状态(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)不是理论,而是调试依据:比如
jstack输出里看到一堆WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject,说明你可能卡在await()没被唤醒 - 避免用
Thread.sleep()模拟“等待”,它不释放锁;真要等条件,用Object.wait()或Condition.await()
先吃透 synchronized,再碰 Lock 和原子类
synchronized 看似简单,但它是理解 Java 内存模型(JMM)的入口。JUC 里的所有高级工具,底层都在和它对齐语义。
-
synchronized锁的是对象监视器(monitor),不是代码块——所以两个线程分别锁obj1和obj2,完全不互斥 - 静态方法上的
synchronized锁的是当前类的Class对象,和实例锁无关;这点常被忽略,导致单例双重检查锁(DCL)写错 -
AtomicInteger不是万能替代品:它只保证单个变量的原子读写,无法替代“读-改-写”复合操作(比如先 get 再 compareAndSet),这种场景仍需锁或StampedLock
线程池必须结合实际任务类型来配参数
照抄网上“核心线程数 = CPU 核数 + 1”会在线上翻车。IO 密集型任务和计算密集型任务的线程池策略完全不同。
- CPU 密集型(如图像处理):线程数 ≈
Runtime.getRuntime().availableProcessors(),太多线程只会增加上下文切换开销 - IO 密集型(如 HTTP 调用):线程数可设为
2 × CPU 核数甚至更高,因为线程常阻塞在 socket read/write 上 -
LinkedBlockingQueue默认无界,用它做工作队列等于把 OOM 风险交给 GC;生产环境务必指定容量,或改用SynchronousQueue(配合newCachedThreadPool)
别绕开 JMM 和 volatile,否则永远看不懂“可见性”问题
“我明明改了值,另一个线程却看不到”——这不是 bug,是你没理解主内存与工作内存的分离。volatile 不是同步器,而是内存屏障的轻量表达。
立即学习“Java免费学习笔记(深入)”;
-
volatile仅保证可见性和禁止指令重排序,**不保证原子性**:volatile int count;的count++仍是三步(读、+1、写),并发下必然丢数据 - 只要涉及“状态标志位”,比如
isRunning控制线程退出,就必须用volatile,否则 JVM 可能优化成死循环(因读取缓存在寄存器) - happens-before 规则不是考题:它直接决定你写的代码在多核 CPU 上是否按预期执行。比如
synchronized块的解锁 happens-before 下一个synchronized块的加锁










