Java 多线程编程
创建时间: 2026-04-15 标签: #Java #多线程 #并发编程 #面试重点
1. 线程基础概念
什么是线程
- 进程(Process):程序的一次执行实例,拥有独立的内存空间
- 线程(Thread):进程内的执行单元,共享进程的内存空间,轻量级
线程 vs 进程
| 特性 | 进程 | 线程 |
|---|---|---|
| 资源占用 | 大 | 小 |
| 创建/销毁 | 慢 | 快 |
| 通信 | 复杂(IPC) | 简单(共享内存) |
| 独立性 | 独立 | 共享进程资源 |
2. 线程创建方式
方式一:继承 Thread 类
java
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
// 使用
MyThread t = new MyThread();
t.start(); // 启动线程(不是 run())方式二:实现 Runnable 接口(推荐)
java
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable 线程执行中");
}
}
// 使用
Thread t = new Thread(new MyRunnable());
t.start();方式三:实现 Callable 接口(带返回值)
java
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 42;
}
}
// 使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // 阻塞等待结果
executor.shutdown();方式四:线程池(生产环境推荐)
java
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> {
// 任务逻辑
System.out.println("线程池任务");
});
executor.shutdown();3. 线程生命周期
┌─────────────────────────────────────────┐
│ NEW │
│ (创建但未启动) │
└─────────────┬───────────────────────────┘
│ start()
▼
┌─────────────────────────────────────────┐
│ RUNNABLE │
│ (可运行,可能正在运行也可能等待CPU) │
└─────────────┬───────────────────────────┘
│
┌───────────┼───────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ RUNNING │ │ BLOCKED │ │ WAITING │
│ (运行中) │ │ (阻塞) │ │ (等待) │
└────┬─────┘ └──────────┘ └──────────┘
│ ▲ ▲
│ │ │
└──────────────┴───────────┘
│
▼
┌─────────────────────────────────────────┐
│ TERMINATED │
│ (结束) │
└─────────────────────────────────────────┘状态说明
| 状态 | 说明 |
|---|---|
| NEW | 线程对象创建,未调用 start() |
| RUNNABLE | 调用 start() 后的就绪/运行状态 |
| BLOCKED | synchronized 锁等待 |
| WAITING | wait(), join(), LockSupport.park() 等待 |
| TIMED_WAITING | 带超时的等待(sleep, join(timeout)) |
| TERMINATED | 线程执行完毕 |
4. 线程同步机制
4.1 synchronized 关键字
java
// 同步方法
public synchronized void method() {
// 线程安全的操作
}
// 同步代码块
public void method() {
synchronized (this) { // 锁对象
// 临界区代码
}
}
// 静态同步方法(锁类对象)
public static synchronized void staticMethod() {
// 锁住的是 Class 对象
}4.2 Lock 接口(显式锁)
java
private final ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 临界区
} finally {
lock.unlock(); // 必须在 finally 中释放
}
}synchronized vs Lock
| 特性 | synchronized | Lock |
|---|---|---|
| 获得锁 | 自动释放 | 手动释放 |
| 尝试非阻塞获取 | ❌ | ✅ tryLock() |
| 超时获取 | ❌ | ✅ tryLock(timeout) |
| 公平锁 | ❌ | ✅ ReentrantLock(true) |
| 条件等待 | wait/notify | Condition |
4.3 volatile 关键字
java
private volatile boolean flag = false;作用:
- 保证可见性:修改对所有线程立即可见
- 禁止指令重排序
⚠️ 注意:volatile 不保证原子性!
4.4 ThreadLocal 线程本地变量
java
private static final ThreadLocal<SimpleDateFormat> formatter =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
// 每个线程独立的对象
String result = formatter.get();5. 线程间通信
5.1 wait/notify/notifyAll
java
public class SharedResource {
private boolean available = false;
public synchronized void produce() {
while (available) {
try {
wait(); // 等待消费者消费
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 生产
available = true;
notify(); // 通知消费者
}
public synchronized void consume() {
while (!available) {
try {
wait(); // 等待生产者生产
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 消费
available = false;
notify(); // 通知生产者
}
}5.2 Condition 条件变量
java
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
public void put(Object item) {
lock.lock();
try {
while (isFull()) {
notFull.await();
}
// 添加元素
notEmpty.signal();
} finally {
lock.unlock();
}
}5.3 BlockingQueue(推荐的生产者-消费者模式)
java
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(100);
// 生产者
queue.put(1); // 阻塞直到队列有空位
// 消费者
Integer item = queue.take(); // 阻塞直到队列有元素6. 线程池
6.1 常用线程池类型
java
// 固定大小线程池
ExecutorService fixed = Executors.newFixedThreadPool(10);
// 缓存线程池(自动扩容)
ExecutorService cached = Executors.newCachedThreadPool();
// 单线程线程池
ExecutorService single = Executors.newSingleThreadExecutor();
// 调度线程池
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);⚠️ 阿里巴巴规范警告:
Executors返回的线程池存在资源耗尽风险,推荐使用ThreadPoolExecutor手动创建。
6.2 ThreadPoolExecutor 详解
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize:核心线程数
10, // maximumPoolSize:最大线程数
60L, // keepAliveTime:空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 任务队列
new ThreadFactory() { // 线程工厂
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("my-pool-" + t.getName());
return t;
}
},
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);6.3 拒绝策略
| 策略 | 行为 |
|---|---|
| AbortPolicy | 抛出 RejectedExecutionException |
| CallerRunsPolicy | 由调用线程执行任务 |
| DiscardPolicy | 静默丢弃 |
| DiscardOldestPolicy | 丢弃队列中最旧的任务 |
7. 高级并发工具
7.1 CountDownLatch(倒计时门闩)
java
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown(); // 计数减一
}).start();
}
latch.await(); // 等待计数归零
System.out.println("所有任务完成");场景:多线程任务汇总、主线程等待子任务完成
7.2 CyclicBarrier(循环栅栏)
java
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("所有线程到达屏障,执行汇总操作")
);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
barrier.await(); // 等待其他线程
}).start();
}场景:多线程分阶段任务、并发测试
7.3 Semaphore(信号量)
java
Semaphore semaphore = new Semaphore(3); // 3个许可
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获取许可
// 访问共享资源
semaphore.release(); // 释放许可
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}场景:限流、连接池、资源访问控制
7.4 Phaser(阶段同步器)
java
Phaser phaser = new Phaser(3); // 3个参与者
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 阶段1
phaser.arriveAndAwaitAdvance();
// 阶段2
phaser.arriveAndAwaitAdvance();
}).start();
}场景:多阶段并行任务
8. 并发集合
java
// 线程安全的 List
List<String> list = new CopyOnWriteArrayList<>();
// 或 Collections.synchronizedList(new ArrayList<>());
// 线程安全的 Map
Map<String, Object> map = new ConcurrentHashMap<>();
// 线程安全的 Set
Set<String> set = new CopyOnWriteArraySet<>();
// 或 Collections.synchronizedSet(new HashSet<>());
// 高性能并发队列
Queue<Integer> queue = new ConcurrentLinkedQueue<>();
BlockingQueue<Integer> bq = new LinkedBlockingQueue<>();并发集合对比
| 集合 | 特点 | 适用场景 |
|---|---|---|
| ConcurrentHashMap | 分段锁/无锁 | 高并发读写的 Map |
| CopyOnWriteArrayList | 写时复制 | 读多写少的 List |
| ConcurrentLinkedQueue | 无锁 CAS | 高并发队列 |
| BlockingQueue | 支持阻塞操作 | 生产者-消费者 |
9. 原子类
java
// 原子整数
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // ++i
counter.getAndIncrement(); // i++
counter.addAndGet(5); // i += 5
// 原子引用
AtomicReference<User> userRef = new AtomicReference<>();
userRef.compareAndSet(oldUser, newUser);
// 原子累加器(高并发,性能优于 AtomicInteger)
LongAdder sum = new LongAdder();
sum.add(100);
long result = sum.sum();10. 最佳实践
✅ 推荐做法
java
// 1. 优先使用线程池,不直接创建线程
ExecutorService executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(), // CPU核心数
100,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 2. 使用 volatile 保证可见性
private volatile boolean shutdown = false;
// 3. 优先使用并发工具类
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100);
// 4. 使用 atomic 类
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
// 5. 异常处理
executor.submit(() -> {
try {
// 任务逻辑
} catch (Exception e) {
log.error("任务执行失败", e);
}
});❌ 避免做法
java
// 1. 不要直接创建线程
// 错误 ❌
new Thread(() -> doWork()).start();
// 2. 不要在锁内执行耗时操作
synchronized (lock) {
Thread.sleep(1000); // 错误:持有锁时睡眠
}
// 3. 不要忽略中断
try {
while (condition) {
// 处理
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 正确处理
}11. 常见面试问题
Q1: synchronized 和 ReentrantLock 区别?
| 对比 | synchronized | ReentrantLock |
|---|---|---|
| 来源 | JVM 内置 | JDK 5+ |
| 释放 | 自动释放 | 手动释放 |
| 公平锁 | ❌ | ✅ |
| 尝试获取锁 | ❌ | ✅ tryLock() |
| 超时获取 | ❌ | ✅ tryLock(timeout) |
| 可中断 | ❌ | ✅ lockInterruptibly() |
| 条件变量 | wait/notify | Condition |
Q2: volatile 和 synchronized 区别?
| 特性 | volatile | synchronized |
|---|---|---|
| 可见性 | ✅ | ✅ |
| 原子性 | ❌ | ✅ |
| 有序性 | 禁止重排序 | 保证 |
| 性能 | 轻量 | 重量 |
Q3: 线程死锁如何避免?
- 避免嵌套锁:尽量减少锁的嵌套
- 固定加锁顺序:多把锁按固定顺序获取
- 使用 tryLock 带超时:避免无限等待
- 减少锁粒度:使用分段锁
- 使用并发工具:ConcurrentHashMap 等
Q4: 生产者-消费者如何实现?
java
// 方式1:BlockingQueue(推荐)
BlockingQueue<Item> queue = new LinkedBlockingQueue<>(100);
// 方式2:wait/notify
synchronized (queue) {
while (queue.isEmpty()) {
queue.wait();
}
Item item = queue.remove();
queue.notifyAll();
}Q5: ThreadLocal 内存泄漏原因?
ThreadLocalMap 中的 Entry 继承 WeakReference<ThreadLocal<?>>,key 是弱引用。但 value 是强引用,如果线程不结束或未调用 remove(),可能导致内存泄漏。
正确做法:
- 使用完手动 remove()
- 使用完置为 null
12. 相关资料
- **Java IO流详解**
- **Java时间类详解**
- **Java包装类详解**
📝 本文档使用 AI 辅助生成,内容基于 Java 多线程编程核心知识点整理。