Java多线程入门指南 🚀
本文从零开始,手把手教你掌握Java多线程编程。重点关注面试高频考点,每个概念都配有生动的代码示例和记忆技巧。无论你是准备面试还是想提升编程技能,这份指南都能帮你快速入门!
📚 学习路线图
第1步:理解基本概念 → 第2步:掌握线程创建 → 第3步:理解线程生命周期
↓ ↓ ↓
第6步:面试真题训练 ← 第5步:线程安全工具 ← 第4步:线程常用API
第1章:什么是线程?为什么要用多线程?
🔍 线程 vs 进程:一个生动的比喻
想象一下你在经营一个餐厅:
🏢 进程 = 整个餐厅(厨房、餐桌、收银台、储物间)
👥 线程 = 餐厅里的工作人员(厨师、服务员、收银员)
为什么这个比喻很重要?
- 进程有自己的独立空间(餐厅独立运营)
- 线程共享进程资源(工作人员共享厨房和设备)
- 线程切换更快(厨师和服务员协作比开新餐厅成本更低)
💡 什么情况下需要多线程?
看看这些实际场景:
// 场景1:用户界面 + 后台计算
// 你的微信聊天界面不会因为下载文件而卡死
Thread uiThread = new Thread(() -> updateUI());
Thread downloadThread = new Thread(() -> downloadFile());
// 场景2:批量处理 + 实时响应
// 电商系统同时处理多个用户订单
for (Order order : orders) {
new Thread(() -> processOrder(order)).start();
}
// 场景3:提高CPU利用率
// 视频播放同时进行音视频解码、字幕加载、网络缓冲
Thread videoThread = new Thread(() -> decodeVideo());
Thread audioThread = new Thread(() -> decodeAudio());
Thread subtitleThread = new Thread(() -> loadSubtitles());
✅ 面试加分点:能说出3个以上的实际应用场景,比死记硬背概念更有说服力!
第2章:创建线程的4种方式(含代码对比)
🎯 方式1:继承Thread类(基础入门)
class MyWorker extends Thread {
@Override
public void run() {
System.out.println("工作中... 线程ID: " + Thread.currentThread().getId());
}
}
// 使用方法
MyWorker worker = new MyWorker();
worker.start(); // 注意:不是worker.run()!
⚠️ 注意事项:
- 单继承局限:Java不支持多继承
- 资源浪费:每个新线程都要新建Thread对象
- 面试陷阱:有人会问为什么不用
run()方法
🎯 方式2:实现Runnable接口(最常用)
class Calculator implements Runnable {
private int result;
@Override
public void run() {
result = 2 + 3; // 模拟计算
System.out.println("计算结果: " + result);
}
}
// 使用方法1:直接创建
Calculator task = new Calculator();
new Thread(task).start();
// 使用方法2:Lambda表达式(推荐)
Runnable lambdaTask = () -> {
System.out.println("Lambda方式创建线程");
};
new Thread(lambdaTask).start();
// 使用方法3:方法引用
new Thread(this::doWork).start();
💡 为什么推荐Runnable?
优点1:可以继承其他类
优点2:多个线程可以共享同一个Runnable实例
优点3:符合"面向接口编程"的设计原则
🎯 方式3:Callable + FutureTask(需要返回值时)
import java.util.concurrent.*;
class DataProcessor implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000); // 模拟耗时操作
return "处理完成!";
}
}
// 使用方法
FutureTask<String> future = new FutureTask<>(new DataProcessor());
new Thread(future).start();
try {
// get()会阻塞直到任务完成
String result = future.get(); // 等待结果
System.out.println(result); // 输出:处理完成!
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
🔑 核心概念对比:
| 方法 | 返回值 | 异常处理 | 适用场景 |
|---|---|---|---|
run() | void | 只能捕获,不能抛出 | 简单任务 |
call() | T | 可以抛出受检异常 | 需要返回值的任务 |
🎯 方式4:线程池(生产环境必备)
import java.util.concurrent.*;
// 创建线程池(推荐方式)
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
Future<String> future1 = executor.submit(() -> {
Thread.sleep(1000);
return "任务1完成";
});
Future<String> future2 = executor.submit(() -> {
Thread.sleep(2000);
return "任务2完成";
});
// 获取结果
System.out.println(future1.get());
System.out.println(future2.get());
// 关闭线程池(重要!)
executor.shutdown();
⚠️ 面试必问:为什么推荐使用线程池?
- 资源复用:避免频繁创建销毁线程的开销
- 控制并发数:防止内存溢出和CPU过载
- 统一管理:提供任务队列、异常处理等功能
第3章:线程生命周期(高频面试题)
🔄 线程的6种状态
📋 状态详解表
| 状态 | 触发方式 | 面试要点 | 实际例子 |
|---|---|---|---|
| NEW | new Thread() | 还没运行,不是真正的线程 | Thread t = new MyThread(); |
| RUNNABLE | start()调用后 | 包含就绪和运行两种状态 | t.start(); |
| BLOCKED | 等待synchronized锁 | 面试常考:阻塞不是等待 | synchronized(lock) { ... } |
| WAITING | wait(), join() | 无限期等待,需要其他线程唤醒 | obj.wait();, t.join(); |
| TIMED_WAITING | sleep(), wait(timeout) | 有时间限制的等待 | Thread.sleep(1000); |
| TERMINATED | run()执行完 | 线程结束,不能重新启动 | 正常执行完毕或异常 |
🎯 面试陷阱:BLOCKED vs WAITING
// BLOCKED状态示例:等待锁
public class BlockedExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized(lock) {
try {
Thread.sleep(5000); // 持有锁5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
synchronized(lock) { // t2会进入BLOCKED状态
System.out.println("获得锁了!");
}
});
t1.start();
t2.start();
System.out.println("t2状态: " + t2.getState()); // BLOCKED
}
}
// WAITING状态示例:调用wait()
public class WaitingExample {
private static final Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
synchronized(lock) {
try {
lock.wait(); // t1会进入WAITING状态
System.out.println("被唤醒了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread.sleep(1000);
System.out.println("t1状态: " + t1.getState()); // WAITING
synchronized(lock) {
lock.notify(); // 唤醒t1
}
}
}
✅ 记忆技巧:
- BLOCKED = 我想工作但拿不到工具(锁)
- WAITING = 我在等别人叫我起床(notify/join)
- TIMED_WAITING = 我定了个闹钟,到点就起(sleep)
第4章:线程常用API(含实用示例)
⏰ sleep() - 暂停当前线程
public class SleepExample {
public static void main(String[] args) {
System.out.println("开始倒计时...");
try {
for (int i = 5; i > 0; i--) {
System.out.println(i + "秒");
Thread.sleep(1000); // 暂停1秒
}
System.out.println("时间到!");
} catch (InterruptedException e) {
System.out.println("睡眠被中断");
Thread.currentThread().interrupt(); // 恢复中断状态
}
}
}
🎯 面试考点:
sleep()不会释放锁(重要!)- 必须处理
InterruptedException - 时间到后自动回到
RUNNABLE状态
🔗 join() - 等待线程结束
public class JoinExample {
public static void main(String[] args) throws Exception {
Thread downloadThread = new Thread(() -> {
try {
System.out.println("开始下载...");
Thread.sleep(3000); // 模拟下载耗时
System.out.println("下载完成!");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread processThread = new Thread(() -> {
try {
System.out.println("开始处理数据...");
Thread.sleep(2000); // 模拟处理耗时
System.out.println("数据处理完成!");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
downloadThread.start();
processThread.start();
// 等待两个线程都完成
downloadThread.join(); // 主线程等待下载完成
processThread.join(); // 主线程等待处理完成
System.out.println("所有任务都完成了!");
}
}
💡 实际应用场景:
- 主线程等待所有子任务完成后再汇总结果
- 批量处理:等待一批文件全部处理完
- 测试场景:确保异步操作完成后再验证结果
⚡ interrupt() - 优雅地停止线程
public class InterruptExample {
public static void main(String[] args) {
Worker worker = new Worker();
Thread workerThread = new Thread(worker);
workerThread.start();
// 主线程等待3秒后中断工作线程
try {
Thread.sleep(3000);
workerThread.interrupt(); // 发送中断信号
System.out.println("主线程发送了中断信号");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class Worker implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("工作中...");
Thread.sleep(1000); // 模拟工作
} catch (InterruptedException e) {
System.out.println("收到中断信号,准备退出");
Thread.currentThread().interrupt(); // 重新设置中断标记
break;
}
}
System.out.println("工作线程已停止");
}
}
}
🎯 核心理解:
interrupt()不是强制杀死线程- 它只是设置一个"中断标记"
- 线程需要自己检查标记并决定如何响应
🤝 wait/notify - 线程间通信
public class WaitNotifyExample {
private static final Object lock = new Object();
private static boolean ready = false;
public static void main(String[] args) {
Thread consumer = new Thread(() -> {
synchronized(lock) {
System.out.println("消费者:等待数据准备...");
while (!ready) { // 用while避免虚假唤醒
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者:开始消费数据");
}
});
Thread producer = new Thread(() -> {
synchronized(lock) {
System.out.println("生产者:准备数据中...");
try {
Thread.sleep(2000); // 模拟数据准备
} catch (InterruptedException e) {
e.printStackTrace();
}
ready = true;
lock.notify(); // 通知消费者
System.out.println("生产者:数据准备完成,已通知消费者");
}
});
consumer.start();
producer.start();
}
}
🔑 关键点:
- 必须在
synchronized块中使用 wait()会释放锁,sleep()不会- 必须用
while循环检查条件,避免虚假唤醒 notifyAll()比notify()更安全
第5章:CompletableFuture - 异步编程的革命 (Java 8+)
CompletableFuture 是 Java 8 引入的革命性特性,它让异步编程变得简单优雅。如果你还在使用传统的回调方式处理异步任务,那么本章将彻底改变你的编程方式!
🎯 为什么需要 CompletableFuture?
传统方式的痛点
// ❌ 传统回调方式 - "回调地狱"
public voidtraditionalCallback() {
downloadFile("file1.pdf", new Callback<File>() {
@Override
public void onSuccess(File file1) {
downloadFile("file2.pdf", new Callback<File>() {
@Override
public void onSuccess(File file2) {
processFiles(file1, file2, new Callback<Result>() {
@Override
public void onSuccess(Result result) {
saveResult(result, new Callback<Boolean>() {
@Override
public void onSuccess(Boolean success) {
System.out.println("任务完成!");
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
}
CompletableFuture 的优雅
// ✅ CompletableFuture - 链式调用,清晰优雅
public void modernAsync() {
CompletableFuture<File> future1 = CompletableFuture.supplyAsync(() -> downloadFile("file1.pdf"));
CompletableFuture<File> future2 = CompletableFuture.supplyAsync(() -> downloadFile("file2.pdf"));
CompletableFuture<Result> resultFuture = future1.thenCombine(future2, (file1, file2) ->
processFiles(file1, file2)
);
resultFuture.thenCompose(result ->
CompletableFuture.supplyAsync(() -> saveResult(result))
).thenAccept(success ->
System.out.println("任务完成!")
).exceptionally(e -> {
e.printStackTrace();
return null;
});
}
🚀 核心优势:
- 链式调用:避免回调地狱
- 组合能力强:轻松组合多个异步任务
- 异常处理统一:一处处理所有异常
- 函数式编程:支持 Lambda 表达式
📚 CompletableFuture 基础操作
1. 创建异步任务
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureBasics {
// 方式1:supplyAsync - 有返回值
public static CompletableFuture<String> fetchData() {
return CompletableFuture.supplyAsync(() -> {
System.out.println("获取数据中... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "数据获取完成";
});
}
// 方式2:runAsync - 无返回值
public static CompletableFuture<Void> processData() {
return CompletableFuture.runAsync(() -> {
System.out.println("处理数据中... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据处理完成");
});
}
// 方式3:指定线程池
private static final ExecutorService customExecutor = Executors.newFixedThreadPool(5);
public static CompletableFuture<String> fetchWithCustomPool() {
return CompletableFuture.supplyAsync(() -> {
System.out.println("使用自定义线程池: " + Thread.currentThread().getName());
return "自定义线程池执行";
}, customExecutor);
}
public static void main(String[] args) throws Exception {
// 测试基本使用
CompletableFuture<String> future = fetchData();
// 方式1:阻塞获取结果
String result = future.get(); // 等待任务完成
System.out.println("结果: " + result);
// 方式2:非阻塞处理
fetchData().thenAccept(data -> {
System.out.println("异步处理结果: " + data);
});
// 等待异步任务完成
Thread.sleep(3000);
customExecutor.shutdown();
}
}
2. 转换和组合操作
public class CompletableFutureTransformations {
public static void transformationExamples() {
// thenApply - 转换结果
CompletableFuture<Integer> numberFuture = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<String> stringFuture = numberFuture.thenApply(n -> {
System.out.println("转换数字: " + n);
return "数字: " + n;
});
// thenAccept - 消费结果(无返回值)
numberFuture.thenAccept(n -> {
System.out.println("消费数字: " + n);
});
// thenRun - 任务完成后执行(不关心之前的结果)
numberFuture.thenRun(() -> {
System.out.println("任务完成后的清理工作");
});
// thenCompose - 链式异步操作(扁平化)
CompletableFuture<String> chainedFuture = numberFuture.thenCompose(n -> {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "链式结果: " + (n * 2);
});
});
}
public static void combinationExamples() {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return 10;
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1500); } catch (InterruptedException e) {}
return 20;
});
// thenCombine - 组合两个异步结果
CompletableFuture<Integer> combined = future1.thenCombine(future2, (a, b) -> {
System.out.println("组合结果: " + a + " + " + b);
return a + b;
});
// thenAcceptBoth - 组合并消费(无返回值)
future1.thenAcceptBoth(future2, (a, b) -> {
System.out.println("消费组合结果: " + a + " 和 " + b);
});
// runAfterBoth - 两个都完成后执行
future1.runAfterBoth(future2, () -> {
System.out.println("两个任务都完成了");
});
// applyToEither - 任一完成就处理
CompletableFuture<Integer> fastFuture = future1.applyToEither(future2, result -> {
System.out.println("快速完成的结果: " + result);
return result * 2;
});
// allOf - 等待所有任务完成
CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2);
allOf.thenRun(() -> {
System.out.println("所有任务都完成了");
});
// anyOf - 任一任务完成
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2);
anyOf.thenAccept(result -> {
System.out.println("某个任务完成了: " + result);
});
}
public static void main(String[] args) throws Exception {
transformationExamples();
combinationExamples();
Thread.sleep(3000); // 等待异步任务完成
}
}
3. 异常处理
public class CompletableFutureExceptions {
public static void exceptionHandling() {
// exceptionally - 处理异常并提供默认值
CompletableFuture<String> safeFuture = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("模拟异常");
}
return "正常结果";
}).exceptionally(ex -> {
System.out.println("捕获异常: " + ex.getMessage());
return "默认结果";
});
// handle - 无论成功失败都处理
CompletableFuture<String> handledFuture = CompletableFuture.supplyAsync(() -> {
return "可能失败的操作";
}).handle((result, ex) -> {
if (ex != null) {
System.out.println("处理异常: " + ex.getMessage());
return "异常处理的默认值";
} else {
System.out.println("正常处理结果: " + result);
return result.toUpperCase();
}
});
// whenComplete - 类似handle但不转换结果
CompletableFuture<String> completedFuture = CompletableFuture.supplyAsync(() -> {
return "最终结果";
}).whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("任务失败: " + ex.getMessage());
} else {
System.out.println("任务成功: " + result);
}
});
}
public static void timeoutHandling() {
// orTimeout - 设置超时时间 (Java 9+)
CompletableFuture<String> timeoutFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000); // 模拟长时间任务
} catch (InterruptedException e) {
e.printStackTrace();
}
return "完成";
}).orTimeout(2, TimeUnit.SECONDS) // 2秒超时
.exceptionally(ex -> {
System.out.println("任务超时: " + ex.getMessage());
return "超时默认值";
});
// completeOnTimeout - 超时后使用默认值 (Java 9+)
CompletableFuture<String> timeoutWithDefault = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "原始结果";
}).completeOnTimeout("超时默认值", 2, TimeUnit.SECONDS);
}
public static void main(String[] args) throws Exception {
exceptionHandling();
timeoutHandling();
Thread.sleep(4000);
}
}
🎯 实战案例:电商订单处理
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ECommerceExample {
private static final ExecutorService businessPool = Executors.newFixedThreadPool(10);
// 模拟用户服务
public static CompletableFuture<User> getUserInfo(String userId) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("获取用户信息... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟数据库查询
} catch (InterruptedException e) {
e.printStackTrace();
}
return new User(userId, "张三", "VIP");
}, businessPool);
}
// 模拟库存服务
public static CompletableFuture<Boolean> checkInventory(String productId) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("检查库存... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Math.random() > 0.2; // 80%概率有库存
}, businessPool);
}
// 模拟价格服务
public static CompletableFuture<Price> calculatePrice(String productId, String userLevel) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("计算价格... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
double basePrice = 100.0;
double discount = userLevel.equals("VIP") ? 0.2 : 0.1;
return new Price(basePrice, discount, basePrice * (1 - discount));
}, businessPool);
}
// 模拟支付服务
public static CompletableFuture<PaymentResult> processPayment(Order order) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("处理支付... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Math.random() > 0.1) { // 90%成功率
return new PaymentResult("SUCCESS", "支付成功", "TXN" + System.currentTimeMillis());
} else {
throw new RuntimeException("支付失败");
}
}, businessPool);
}
// 完整的订单处理流程
public static CompletableFuture<OrderResult> processOrder(String userId, String productId) {
// 并行获取用户信息和检查库存
CompletableFuture<User> userFuture = getUserInfo(userId);
CompletableFuture<Boolean> inventoryFuture = checkInventory(productId);
// 等待用户信息和库存检查都完成
return userFuture.thenCombine(inventoryFuture, (user, hasInventory) -> {
if (!hasInventory) {
throw new RuntimeException("库存不足");
}
return new Order(user, productId, 1);
}).thenCompose(order ->
// 有库存后计算价格
calculatePrice(order.productId, order.user.level)
.thenApply(price -> {
order.price = price;
return order;
})
).thenCompose(order ->
// 处理支付
processPayment(order).thenApply(payment -> {
return new OrderResult(order, payment);
})
).exceptionally(ex -> {
// 统一异常处理
System.err.println("订单处理失败: " + ex.getMessage());
return new OrderResult(null, null, ex.getMessage());
});
}
public static void main(String[] args) throws Exception {
System.out.println("=== 电商订单处理示例 ===");
// 模拟多个订单处理
CompletableFuture<OrderResult> order1 = processOrder("user001", "prod001");
CompletableFuture<OrderResult> order2 = processOrder("user002", "prod002");
CompletableFuture<OrderResult> order3 = processOrder("user003", "prod003");
// 处理结果
order1.thenAccept(result -> {
if (result.success) {
System.out.println("订单1成功: " + result);
} else {
System.err.println("订单1失败: " + result.errorMessage);
}
});
order2.thenAccept(result -> {
if (result.success) {
System.out.println("订单2成功: " + result);
} else {
System.err.println("订单2失败: " + result.errorMessage);
}
});
order3.thenAccept(result -> {
if (result.success) {
System.out.println("订单3成功: " + result);
} else {
System.err.println("订单3失败: " + result.errorMessage);
}
});
// 等待所有订单处理完成
CompletableFuture<Void> allOrders = CompletableFuture.allOf(order1, order2, order3);
allOrders.thenRun(() -> {
System.out.println("=== 所有订单处理完成 ===");
businessPool.shutdown();
});
// 等待处理完成
Thread.sleep(10000);
}
// 数据模型类
static class User {
String id, name, level;
User(String id, String name, String level) {
this.id = id; this.name = name; this.level = level;
}
@Override
public String toString() {
return String.format("User{id=%s, name=%s, level=%s}", id, name, level);
}
}
static class Price {
double basePrice, discount, finalPrice;
Price(double basePrice, double discount, double finalPrice) {
this.basePrice = basePrice; this.discount = discount; this.finalPrice = finalPrice;
}
@Override
public String toString() {
return String.format("Price{基础=%.2f, 折扣=%.0f%%, 最终=%.2f}",
basePrice, discount * 100, finalPrice);
}
}
static class Order {
User user;
String productId;
int quantity;
Price price;
Order(User user, String productId, int quantity) {
this.user = user; this.productId = productId; this.quantity = quantity;
}
}
static class PaymentResult {
String status, message, transactionId;
PaymentResult(String status, String message, String transactionId) {
this.status = status; this.message = message; this.transactionId = transactionId;
}
@Override
public String toString() {
return String.format("PaymentResult{状态=%s, 消息=%s, 交易ID=%s}",
status, message, transactionId);
}
}
static class OrderResult {
boolean success;
Order order;
PaymentResult payment;
String errorMessage;
OrderResult(Order order, PaymentResult payment) {
this.success = true;
this.order = order;
this.payment = payment;
}
OrderResult(Order order, PaymentResult payment, String errorMessage) {
this.success = false;
this.order = order;
this.payment = payment;
this.errorMessage = errorMessage;
}
@Override
public String toString() {
if (success) {
return String.format("OrderResult{订单=%s, 支付=%s}", order.user, payment);
} else {
return String.format("OrderResult{错误=%s}", errorMessage);
}
}
}
}
💡 最佳实践与性能优化
1. 线程池选择
public class ThreadPoolBestPractices {
// ✅ 推荐:使用自定义线程池
private static final ExecutorService cpuIntensivePool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static final ExecutorService ioIntensivePool =
Executors.newCachedThreadPool();
// ❌ 避免:直接使用ForkJoinPool.commonPool()
public static void badExample() {
// 这会阻塞公共线程池,影响其他操作
CompletableFuture.supplyAsync(() -> {
// CPU密集型任务
return heavyComputation();
});
}
// ✅ 推荐:为不同类型任务使用专用线程池
public static void goodExample() {
// CPU密集型任务
CompletableFuture.supplyAsync(() -> {
return heavyComputation();
}, cpuIntensivePool);
// IO密集型任务
CompletableFuture.supplyAsync(() -> {
return fileRead();
}, ioIntensivePool);
}
private static int heavyComputation() {
// 模拟CPU密集型计算
return 0;
}
private static String fileRead() {
// 模拟IO密集型操作
return "file content";
}
}
2. 避免阻塞操作
public class NonBlockingPatterns {
// ❌ 错误:在CompletableFuture中阻塞
public static CompletableFuture<String> blockingExample() {
return CompletableFuture.supplyAsync(() -> {
try {
// 这样会阻塞线程池中的线程
Thread.sleep(5000);
return "完成";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
// ✅ 正确:使用调度器处理延迟
public static CompletableFuture<String> nonBlockingExample() {
CompletableFuture<String> future = new CompletableFuture<>();
// 使用延迟调度器
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() -> {
future.complete("完成");
scheduler.shutdown();
}, 5, TimeUnit.SECONDS);
return future;
}
// ✅ 更好的方式:使用异步IO
public static CompletableFuture<String> asyncIOExample() {
return CompletableFuture.supplyAsync(() -> {
// 使用NIO或其他异步IO方式
return performAsyncFileRead();
});
}
private static String performAsyncFileRead() {
return "file content";
}
}
第6章:线程安全工具选择指南
🛡️ synchronized - 最简单的保护
public class BankAccount {
private int balance = 1000;
// synchronized方法
public synchronized void deposit(int amount) {
balance += amount;
System.out.println("存款 " + amount + ",余额: " + balance);
}
// synchronized代码块(推荐,控制更精确)
public void withdraw(int amount) {
synchronized(this) {
if (balance >= amount) {
try {
Thread.sleep(100); // 模拟处理时间
balance -= amount;
System.out.println("取款 " + amount + ",余额: " + balance);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("余额不足,无法取款 " + amount);
}
}
}
public int getBalance() {
return balance;
}
}
✅ 什么时候用synchronized?
- 临界区代码简单
- 不需要超时或中断功能
- 锁竞争不激烈
⚡ volatile - 保证可见性
public class VolatileExample {
// volatile保证了可见性:一个线程修改,其他线程立即可见
private volatile boolean running = true;
public void startWorker() {
new Thread(() -> {
while (running) {
System.out.println("工作中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("工作线程已停止");
}).start();
}
public void stopWorker() {
running = false; // 修改会立即对其他线程可见
System.out.println("发送停止信号");
}
public static void main(String[] args) throws Exception {
VolatileExample example = new VolatileExample();
example.startWorker();
Thread.sleep(3000);
example.stopWorker();
}
}
⚠️ volatile的局限性:
// ❌ 错误示例:volatile不能保证原子性
public class Counter {
private volatile int count = 0;
public void increment() {
count++; // 这不是原子操作!会导致数据不一致
// 实际包含三步:1.读取count 2.加1 3.写回count
}
}
// ✅ 正确做法:使用AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class SafeCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 原子操作
}
public int getCount() {
return count.get();
}
}
🎯 选择工具的决策树
需要保护复合操作吗?
├─ 是 → 需要同步机制
│ ├─ 简单场景,临界区短? → synchronized
│ ├─ 需要超时、中断、公平锁? → ReentrantLock
│ └─ 读多写少? → ReadWriteLock
└─ 否 → 只需要保证可见性?
├─ 简单状态标记? → volatile
└─ 复合操作但需要原子性? → Atomic类
第6章:高频面试真题(附答题技巧)
🎪 面试题1:start() vs run()的区别
❌ 错误回答: "start()是启动线程,run()是执行线程"(太笼统)
✅ 标准回答:
// 1. start():创建新线程并调用run()
Thread thread = new Thread(() -> System.out.println("新线程执行"));
thread.start(); // JVM会创建新线程来执行run()
// 2. run():普通方法调用,在当前线程执行
thread.run(); // 只是在当前线程中执行run()方法,没有创建新线程
💡 答题技巧:
- 强调"线程创建"的区别
- 提到start()只能调用一次
- 可以结合源码说JVM底层实现
🎪 面试题2:sleep() vs wait()的区别
| 对比项 | sleep() | wait() |
|---|---|---|
| 所属类 | Thread类 | Object类 |
| 释放锁 | ❌ 不释放 | ✅ 释放锁 |
| 使用位置 | 任何地方 | synchronized块内 |
| 唤醒方式 | 时间到自动醒 | notify()/notifyAll() |
| 异常处理 | InterruptedException | InterruptedException |
💡 记忆技巧:
sleep()= 我累了睡会儿,手还抓着东西(锁)wait()= 我把东西放下等别人叫我
🎪 面试题3:如何安全停止线程?
❌ 危险做法:
// 不要这样做!
thread.stop(); // 已废弃,不安全
✅ 正确做法:
public class GracefulShutdown implements Runnable {
private volatile boolean running = true;
@Override
public void run() {
while (running && !Thread.currentThread().isInterrupted()) {
try {
doWork();
} catch (InterruptedException e) {
// 被中断,准备退出
Thread.currentThread().interrupt();
break;
}
}
cleanup(); // 清理资源
}
public void shutdown() {
running = false;
}
private void doWork() throws InterruptedException {
// 模拟工作
Thread.sleep(100);
}
private void cleanup() {
System.out.println("清理资源");
}
}
🎪 面试题4:synchronized锁的是什么?
public class LockObjectExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized(lock1) { // 锁住lock1对象
System.out.println("Method1执行中");
}
}
public void method2() {
synchronized(this) { // 锁住当前实例
System.out.println("Method2执行中");
}
}
public synchronized void method3() { // 等价于synchronized(this)
System.out.println("Method3执行中");
}
public static synchronized void method4() { // 锁住Class对象
System.out.println("Method4执行中");
}
}
💡 面试加分点:
- 说出四种锁对象:this、Class对象、任意对象、synchronized方法
- 理解锁是对象级别的,不是方法级别的
- 知道不同锁对象之间不会互斥
🎪 面试题5:CompletableFuture vs Future 区别?
❌ 表面回答: "CompletableFuture是Future的升级版"(太简单)
✅ 深度回答:
// Future的局限性
public Future<String> downloadFile() {
return executor.submit(() -> {
Thread.sleep(3000);
return "文件内容";
});
}
// ❌ Future的问题:阻塞获取结果
Future<String> future = downloadFile();
while (!future.isDone()) {
// 只能轮询,浪费CPU
Thread.sleep(100);
}
String content = future.get(); // 阻塞等待
// ✅ CompletableFuture的优势:非阻塞链式调用
public CompletableFuture<String> downloadFileAsync() {
return CompletableFuture.supplyAsync(() -> {
Thread.sleep(3000);
return "文件内容";
});
}
downloadFileAsync()
.thenApply(content -> content.toUpperCase()) // 转换结果
.thenCompose(upperContent -> processContent(upperContent)) // 链式异步
.thenAccept(processed -> System.out.println("处理完成: " + processed))
.exceptionally(ex -> {
System.err.println("下载失败: " + ex.getMessage());
return null;
});
🔑 核心对比:
| 特性 | Future | CompletableFuture |
|---|---|---|
| 获取结果 | get()阻塞 | thenApply()非阻塞 |
| 组合能力 | ❌ 不支持 | ✅ thenCombine等 |
| 异常处理 | 只能捕获 | exceptionally优雅处理 |
| 链式调用 | ❌ 不支持 | ✅ 流畅的API |
| 任务编排 | 复杂 | 简单直观 |
🎪 面试题6:CompletableFuture的线程池选择?
❌ 错误做法:
// 不要这样做!
CompletableFuture.supplyAsync(() -> {
// CPU密集型任务阻塞了公共线程池
return heavyComputation();
});
✅ 正确做法:
// 1. 为不同类型任务创建专用线程池
ExecutorService cpuPool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors());
ExecutorService ioPool = Executors.newCachedThreadPool();
// 2. CPU密集型任务使用固定线程池
CompletableFuture.supplyAsync(() -> {
return heavyComputation();
}, cpuPool);
// 3. IO密集型任务使用缓存线程池
CompletableFuture.supplyAsync(() -> {
return readFileFromDisk();
}, ioPool);
// 4. 超时控制 (Java 9+)
CompletableFuture.supplyAsync(() -> {
return networkRequest();
}).orTimeout(5, TimeUnit.SECONDS)
.exceptionally(ex -> "请求超时");
💡 面试加分点:
- 说明默认使用
ForkJoinPool.commonPool()的优缺点 - 理解不同类型任务的线程池选择策略
- 知道线程池大小设置的经验公式
🎪 面试题7:如何处理CompletableFuture异常?
❌ 不优雅的处理:
public void handleExceptionPoorly() {
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("任务失败");
}).thenAccept(result -> {
System.out.println(result);
});
// 异常被"吞掉"了!
}
✅ 专业的异常处理:
public void handleExceptionProfessionally() {
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("随机失败");
}
return "成功结果";
})
.handle((result, ex) -> { // 最推荐:无论成功失败都处理
if (ex != null) {
System.out.println("处理异常: " + ex.getMessage());
return "默认结果";
} else {
System.out.println("正常结果: " + result);
return result;
}
})
.thenApply(handledResult -> {
// 继续处理,无论之前是否成功
return handledResult.toUpperCase();
})
.thenAccept(finalResult -> {
System.out.println("最终结果: " + finalResult);
});
}
// 优雅的超时处理
public CompletableFuture<String> timeoutHandling() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000); // 模拟长时间任务
return "任务完成";
} catch (InterruptedException e) {
throw new RuntimeException("任务被中断", e);
}
})
.completeOnTimeout("超时默认值", 2, TimeUnit.SECONDS) // 超时使用默认值
.orTimeout(3, TimeUnit.SECONDS) // 超时抛出异常
.exceptionally(ex -> {
System.out.println("异常处理: " + ex.getMessage());
return "异常处理的默认值";
});
}
🎪 面试题8:什么是线程安全?如何保证?
✅ 保证方法:
// 1. 不可变对象
public final class ImmutablePerson {
private final String name;
private final int age;
public ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}
// 只有getter方法,没有setter方法
public String getName() { return name; }
public int getAge() { return age; }
}
// 2. 线程封闭(ThreadLocal)
public class ThreadLocalExample {
private static ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public String formatDate(Date date) {
return dateFormat.get().format(date);
}
}
// 3. 同步机制
public class SafeCounter {
private final Object lock = new Object();
private int count = 0;
public void increment() {
synchronized(lock) {
count++;
}
}
}
第7章:实战技巧与最佳实践
🚨 常见陷阱及解决方案
陷阱1:不正确的volatile使用
// ❌ 错误
public class BrokenCounter {
private volatile int count = 0;
public void increment() {
count++; // 不是原子操作!
}
}
// ✅ 正确
public class SafeCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
}
陷阱2:死锁的避免
// ❌ 容易死锁的写法
public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized(lock1) {
synchronized(lock2) { // t1持有lock1,等待lock2
System.out.println("t1执行");
}
}
});
Thread t2 = new Thread(() -> {
synchronized(lock2) {
synchronized(lock1) { // t2持有lock2,等待lock1
System.out.println("t2执行");
}
}
});
t1.start();
t2.start();
}
}
// ✅ 避免死锁:统一锁的获取顺序
public class NoDeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
// 统一按顺序获取锁:先lock1再lock2
synchronized(lock1) {
synchronized(lock2) {
System.out.println("t1执行");
}
}
});
Thread t2 = new Thread(() -> {
// 同样按顺序:先lock1再lock2
synchronized(lock1) {
synchronized(lock2) {
System.out.println("t2执行");
}
}
});
t1.start();
t2.start();
}
}
陷阱3:过度同步
// ❌ 过度同步,影响性能
public class OverSyncExample {
private int count = 0;
public synchronized void increment() {
count++; // 同步范围过大
}
public synchronized int getCount() {
return count; // 读取操作不需要同步
}
}
// ✅ 精确同步范围
public class PreciseSyncExample {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized(lock) {
count++; // 只同步必要的代码
}
}
public int getCount() {
// int的读取在32位JVM上是原子的,如果不需要精确的实时一致性
// 可以考虑不同步,或者使用AtomicInteger
synchronized(lock) {
return count;
}
}
}
🔧 性能优化技巧
1. 使用线程池控制并发度
// ❌ 无限制创建线程
for (int i = 0; i < 10000; i++) {
new Thread(() -> doWork()).start(); // 可能导致OOM
}
// ✅ 使用线程池
ExecutorService pool = Executors.newFixedThreadPool(10); // 最多10个线程
for (int i = 0; i < 10000; i++) {
pool.submit(() -> doWork());
}
pool.shutdown();
2. 选择合适的并发集合
// ❌ 使用Collections.synchronizedList
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (String item : list) { // 迭代时仍需手动同步
System.out.println(item);
}
// ✅ 使用CopyOnWriteArrayList(读多写少场景)
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (String item : list) { // 迭代时无需同步
System.out.println(item);
}
// ✅ 使用ConcurrentHashMap(高并发Map)
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1); // 线程安全的put操作
📋 面试速记表
🎯 核心概念速记
| 概念 | 核心要点 | 面试关键词 |
|---|---|---|
| 线程创建 | 4种方式:继承Thread、实现Runnable、Callable、线程池 | 推荐Runnable,生产用线程池 |
| 生命周期 | 6种状态:NEW→RUNNABLE→BLOCKED/WAITING/TIMED_WAITING→TERMINATED | 面试常考状态转换 |
| synchronized | 对象锁,自动释放,简单易用 | 锁对象、锁范围、锁升级 |
| volatile | 保证可见性,不保证原子性 | 适用状态标记,避免复合操作 |
| ThreadLocal | 线程本地变量,每个线程独立副本 | 避免参数传递,存储线程上下文 |
| 线程池 | 复用线程,控制并发度,统一管理 | ThreadPoolExecutor参数配置 |
| CompletableFuture | 异步编程,链式调用,组合任务 | supplyAsync, thenApply, thenCombine |
🚨 常见错误避免
- 不要直接调用
run()方法 - 不要使用
Thread.stop()停止线程 - 不要在构造函数中启动线程
- 不要过度同步,影响性能
- 不要忘记处理线程中断异常
- 不要在synchronized中使用
wait()而不循环检查条件 - 不要在CompletableFuture中使用阻塞操作阻塞线程池
- 不要忘记为不同类型任务选择合适的线程池
💡 面试加分技巧
- 能举例说明:每个概念都能说出1-2个实际应用场景
- 理解底层原理:知道synchronized如何实现,volatile的内存语义
- 了解性能影响:知道不同同步方式的性能特点
- 会排查问题:知道如何使用jstack分析线程状态
- 最佳实践意识:知道何时用什么工具,如何避免常见陷阱
- 现代Java并发:熟练使用CompletableFuture进行异步编程,理解函数式编程思想
🎓 学习建议
📖 进阶学习路线
当前阶段:掌握线程基础
↓
下一阶段:深入学习锁机制(synchronized原理、Lock接口、AQS)
↓
再下一阶段:并发工具类(CountDownLatch、CyclicBarrier、Semaphore)
↓
高级阶段:并发集合、线程池深入、JMM深入理解
↓
实战阶段:设计并发系统、性能调优、问题排查
🛠️ 实践建议
- 动手写代码:每个概念都要亲自实践
- 多线程调试:学会使用断点、jstack等工具
- 性能测试:理解不同方案的性能差异
- 阅读源码:深入理解JDK并发类的设计
- 总结笔记:建立自己的知识体系
🎉 总结
恭喜你!通过这份指南,你已经掌握了Java多线程的核心知识:
✅ 理解了线程的概念和应用场景 ✅ 学会了创建线程的4种方式 ✅ 掌握了线程生命周期和状态转换 ✅ 熟悉了常用的线程API ✅ 了解了线程安全工具的选择 ✅ 攻克了高频面试题
下一步行动建议:
记住:多线程编程是理论与实践相结合的技能,多写多练才能真正掌握!
🌟 如果觉得有帮助,建议收藏本文,面试前快速回顾!