188 lines
4.4 KiB
Markdown
188 lines
4.4 KiB
Markdown
---
|
|
title: "Java 并发编程完整指南:从线程基础到 JUC 进阶"
|
|
date: "2026-05-26"
|
|
category: "后端开发"
|
|
tags: ["Java", "并发", "JUC"]
|
|
excerpt: "全面梳理 Java 并发编程知识体系,从线程基础、synchronized 原理到 JUC 并发工具类。"
|
|
---
|
|
|
|
## 一、线程基础知识
|
|
|
|
### 1.1 线程的生命周期
|
|
|
|
Java 线程在它的生命周期中会经历六种状态:
|
|
|
|
- **NEW** — 线程刚创建,尚未调用 `start()`
|
|
- **RUNNABLE** — 线程在 JVM 中运行
|
|
- **BLOCKED** — 线程被阻塞,等待获取锁
|
|
- **WAITING** — 无限期等待
|
|
- **TIMED_WAITING** — 超时等待
|
|
- **TERMINATED** — 线程已执行完毕
|
|
|
|
> 重要区别:BLOCKED 是在等待进入 synchronized 块时发生的,而 WAITING 是通过 Object.wait()、Thread.join() 等方法进入的。
|
|
|
|
### 1.2 线程的创建方式
|
|
|
|
Java 中创建线程主要有四种方式:
|
|
|
|
**继承 Thread 类**
|
|
|
|
```java
|
|
class MyThread extends Thread {
|
|
@Override
|
|
public void run() {
|
|
System.out.println("线程运行中: " + Thread.currentThread().getName());
|
|
}
|
|
}
|
|
MyThread thread = new MyThread();
|
|
thread.start();
|
|
```
|
|
|
|
**实现 Runnable 接口**
|
|
|
|
```java
|
|
class MyRunnable implements Runnable {
|
|
@Override
|
|
public void run() {
|
|
System.out.println("线程运行中");
|
|
}
|
|
}
|
|
Thread thread = new Thread(new MyRunnable());
|
|
thread.start();
|
|
```
|
|
|
|
**Callable 与 Future**
|
|
|
|
```java
|
|
class MyCallable implements Callable<String> {
|
|
@Override
|
|
public String call() throws Exception {
|
|
Thread.sleep(1000);
|
|
return "任务完成";
|
|
}
|
|
}
|
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
Future<String> future = executor.submit(new MyCallable());
|
|
String result = future.get();
|
|
```
|
|
|
|
## 二、synchronized 原理
|
|
|
|
### 2.1 对象头与 Monitor
|
|
|
|
Java 中的每个对象都与一个 Monitor 关联。`synchronized` 关键字的底层实现依赖于对象头中的 Mark Word 和 Monitor 机制。
|
|
|
|
- **Mark Word**:存储对象的 HashCode、GC 分代年龄、锁状态标志
|
|
- **Monitor**:包含 EntryList、WaitSet、Owner 三个关键部分
|
|
|
|
```java
|
|
public class Counter {
|
|
private int count = 0;
|
|
|
|
public synchronized void increment() {
|
|
count++;
|
|
}
|
|
|
|
public void incrementWithBlock() {
|
|
synchronized (this) {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2.2 锁升级过程
|
|
|
|
JDK 1.6 之后,锁不再直接膨胀为重量级锁,而是逐步升级:
|
|
|
|
| 锁状态 | 适用场景 | 实现原理 |
|
|
|--------|----------|----------|
|
|
| 偏向锁 | 只有一个线程访问 | Mark Word 记录线程 ID |
|
|
| 轻量级锁 | 多线程交替执行 | CAS + 自旋 |
|
|
| 重量级锁 | 竞争激烈 | OS Mutex Lock |
|
|
|
|
## 三、JUC 并发工具
|
|
|
|
### 3.1 ReentrantLock
|
|
|
|
比 synchronized 更灵活的锁实现:
|
|
|
|
```java
|
|
ReentrantLock lock = new ReentrantLock(true);
|
|
lock.lock();
|
|
try {
|
|
// 临界区代码
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
```
|
|
|
|
### 3.2 CountDownLatch
|
|
|
|
等待其他线程完成操作:
|
|
|
|
```java
|
|
CountDownLatch latch = new CountDownLatch(3);
|
|
for (int i = 0; i < 3; i++) {
|
|
new Thread(() -> {
|
|
// 执行任务...
|
|
latch.countDown();
|
|
}).start();
|
|
}
|
|
latch.await();
|
|
```
|
|
|
|
### 3.3 Semaphore
|
|
|
|
控制同时访问资源的线程数量:
|
|
|
|
```java
|
|
Semaphore semaphore = new Semaphore(5);
|
|
semaphore.acquire();
|
|
try {
|
|
// 最多5个线程同时执行
|
|
} finally {
|
|
semaphore.release();
|
|
}
|
|
```
|
|
|
|
## 四、线程池原理
|
|
|
|
### 4.1 核心参数
|
|
|
|
```java
|
|
public ThreadPoolExecutor(
|
|
int corePoolSize, // 核心线程数
|
|
int maximumPoolSize, // 最大线程数
|
|
long keepAliveTime, // 空闲线程存活时间
|
|
TimeUnit unit,
|
|
BlockingQueue<Runnable> workQueue,
|
|
ThreadFactory threadFactory,
|
|
RejectedExecutionHandler handler
|
|
)
|
|
```
|
|
|
|
工作流程:核心线程 → 任务队列 → 最大线程 → 拒绝策略
|
|
|
|
### 4.2 拒绝策略
|
|
|
|
- **AbortPolicy** — 抛出异常
|
|
- **CallerRunsPolicy** — 由提交线程自己执行
|
|
- **DiscardPolicy** — 静默丢弃
|
|
- **DiscardOldestPolicy** — 丢弃最早的未处理任务
|
|
|
|
## 五、虚拟线程
|
|
|
|
Java 21 正式引入了虚拟线程:
|
|
|
|
```java
|
|
Thread vThread = Thread.ofVirtual()
|
|
.name("virtual-thread-1")
|
|
.start(() -> {
|
|
System.out.println("虚拟线程运行中");
|
|
});
|
|
```
|
|
|
|
> 传统"一个请求一个线程"模式在虚拟线程诞生后变得不再昂贵。对于 IO 密集型应用,虚拟线程可以显著提升吞吐量。
|
|
|