Java 并发编程与 JUC
更新时间:2026-08-25。本文是
languages/java/主题第 ③ 篇。Java 并发是后端高并发的根基——synchronized、volatile、JUC 工具包让多线程安全协作。底层原理与本站 L4 内存序 完全同源。
本文要回答的问题
- 多线程的三大问题是什么?
volatile和synchronized各解决什么? - JUC 的原子类/锁/并发容器怎么选?
- 线程池参数怎么定?
一、多线程的三大问题
| 问题 | 含义 | Java 解决 |
|---|---|---|
| 原子性 | 操作不可分割 | synchronized / 原子类 |
| 可见性 | 一个线程的修改对另一个可见 | volatile / synchronized |
| 有序性 | 指令不被重排 | volatile / happens-before |
java
// 可见性问题:flag 是普通变量,Reader 可能永远看不到写
class Flag { boolean running = true; }
// 修复:volatile 保证可见性 + 禁止重排
class Flag { volatile boolean running = true; }二、线程基础
java
// 方式一:继承 Thread
class Worker extends Thread { public void run() { /* ... */ } }
// 方式二:实现 Runnable(推荐,可再继承)
Runnable task = () -> { System.out.println(Thread.currentThread().getName()); };
new Thread(task, "worker").start();线程生命周期:NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED。
三、synchronized:内置锁
java
class Counter {
private int count = 0;
public synchronized void inc() { count++; } // 加锁方法
}- 修饰方法:锁当前对象
this;修饰静态方法:锁类对象。 - 修饰代码块:
synchronized(obj) { ... },可细粒度控制。 - 可重入:同一线程可重复获取同一把锁。
3.1 锁升级(synchronized 的优化)
JVM 对 synchronized 做了锁升级,代价从低到高:
| 状态 | 场景 | 说明 |
|---|---|---|
| 无锁 | 无竞争 | 直接执行 |
| 偏向锁 | 单线程反复获取 | 记录线程 ID |
| 轻量级锁 | 少量竞争 | CAS 自旋 |
| 重量级锁 | 竞争激烈 | 阻塞 + 上下文切换 |
本站视角:轻量级锁的自旋消耗 CPU,重量级锁的阻塞消耗上下文切换——与 L2 调度 的线程切换代价呼应。
四、JUC 原子类与 AQS
4.1 Atomic 原子类(无锁)
java
AtomicInteger ai = new AtomicInteger(0);
ai.incrementAndGet(); // CAS 自增基于 CAS(Compare-And-Swap)+ 自旋,无锁实现原子操作,比 synchronized 轻量。底层映射到 CPU 的 cmpxchg 指令——与 C++ std::atomic 同源,见 L4 原子操作。
4.2 AQS:JUC 锁的基石
ReentrantLock、Semaphore、CountDownLatch 都基于 AQS(AbstractQueuedSynchronizer)——用volatile state + CLH 等待队列实现同步状态管理。
java
Lock lock = new ReentrantLock();
lock.lock();
try { /* 临界区 */ }
finally { lock.unlock(); } // 必须 finally 释放五、线程池
5.1 为什么不裸 new Thread
线程创建/销毁开销大,且无界创建会 OOM。用线程池复用线程:
java
ExecutorService pool = Executors.newFixedThreadPool(8);
pool.submit(() -> { /* 任务 */ });
pool.shutdown();5.2 参数怎么定
| 参数 | 作用 | 建议 |
|---|---|---|
| corePoolSize | 常驻核心线程 | CPU 密集≈核数,IO 密集≈核数×2~3 |
| maximumPoolSize | 最大线程 | 上限 |
| keepAliveTime | 非核心线程空闲回收 | 60s |
| workQueue | 任务队列 | 有界队列防 OOM |
| handler | 拒绝策略 | 自定义/丢弃 |
本站视角:线程数过多会引发上下文切换开销与锁竞争,见 L2 调度 与 Go 的 GMP 对比。
六、并发容器
| 容器 | 替代 | 特点 |
|---|---|---|
ConcurrentHashMap | HashMap | 分段/红黑树,高并发 |
CopyOnWriteArrayList | ArrayList | 读多写少 |
BlockingQueue | Queue | 生产-消费 |
ConcurrentLinkedQueue | LinkedList | 无锁队列 |
七、与本站主线衔接
- 底层原理:volatile/原子类/CAS 的内存序本质,见 L4 内存序与原子操作。
- 锁与性能:锁竞争、自旋、上下文切换的开销与无锁设计,见 低延时设计模式(lockfree)。
- 对比 Go:Java 线程(内核线程)vs Go goroutine(用户态调度),见 Go 并发模型。
一句话总结
Java 并发 = volatile 保可见、synchronized/锁保互斥、原子类 CAS 无锁化、线程池复用线程、JUC 容器保并发安全;底层全是内存序与原子操作,与本站底层原理同源。
上一节:JVM 内存模型与对象 下一节:Stream 与函数式编程