Java并发编程必须夯实基础:先掌握线程生命周期、synchronized与JMM,再学JUC;否则无法定位90%的并发bug。
Java并发编程不能跳着学,先搞懂线程生命周期再碰 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 密集型任务和计算密集型任务的线程池策略完全不同。