跳到主要内容

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();

⚠️ 面试必问:为什么推荐使用线程池?

  1. 资源复用:避免频繁创建销毁线程的开销
  2. 控制并发数:防止内存溢出和CPU过载
  3. 统一管理:提供任务队列、异常处理等功能

第3章:线程生命周期(高频面试题)

🔄 线程的6种状态

📋 状态详解表

状态触发方式面试要点实际例子
NEWnew Thread()还没运行,不是真正的线程Thread t = new MyThread();
RUNNABLEstart()调用后包含就绪和运行两种状态t.start();
BLOCKED等待synchronized锁面试常考:阻塞不是等待synchronized(lock) { ... }
WAITINGwait(), join()无限期等待,需要其他线程唤醒obj.wait();, t.join();
TIMED_WAITINGsleep(), wait(timeout)有时间限制的等待Thread.sleep(1000);
TERMINATEDrun()执行完线程结束,不能重新启动正常执行完毕或异常

🎯 面试陷阱: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();
}
}

🔑 关键点

  1. 必须synchronized块中使用
  2. wait()会释放锁,sleep()不会
  3. 必须while循环检查条件,避免虚假唤醒
  4. 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()
异常处理InterruptedExceptionInterruptedException

💡 记忆技巧

  • 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;
});

🔑 核心对比

特性FutureCompletableFuture
获取结果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

🚨 常见错误避免

  1. 不要直接调用run()方法
  2. 不要使用Thread.stop()停止线程
  3. 不要在构造函数中启动线程
  4. 不要过度同步,影响性能
  5. 不要忘记处理线程中断异常
  6. 不要在synchronized中使用wait()而不循环检查条件
  7. 不要在CompletableFuture中使用阻塞操作阻塞线程池
  8. 不要忘记为不同类型任务选择合适的线程池

💡 面试加分技巧

  1. 能举例说明:每个概念都能说出1-2个实际应用场景
  2. 理解底层原理:知道synchronized如何实现,volatile的内存语义
  3. 了解性能影响:知道不同同步方式的性能特点
  4. 会排查问题:知道如何使用jstack分析线程状态
  5. 最佳实践意识:知道何时用什么工具,如何避免常见陷阱
  6. 现代Java并发:熟练使用CompletableFuture进行异步编程,理解函数式编程思想

🎓 学习建议

📖 进阶学习路线

当前阶段:掌握线程基础

下一阶段:深入学习锁机制(synchronized原理、Lock接口、AQS)

再下一阶段:并发工具类(CountDownLatch、CyclicBarrier、Semaphore)

高级阶段:并发集合、线程池深入、JMM深入理解

实战阶段:设计并发系统、性能调优、问题排查

🛠️ 实践建议

  1. 动手写代码:每个概念都要亲自实践
  2. 多线程调试:学会使用断点、jstack等工具
  3. 性能测试:理解不同方案的性能差异
  4. 阅读源码:深入理解JDK并发类的设计
  5. 总结笔记:建立自己的知识体系

🎉 总结

恭喜你!通过这份指南,你已经掌握了Java多线程的核心知识:

理解了线程的概念和应用场景 ✅ 学会了创建线程的4种方式 ✅ 掌握了线程生命周期和状态转换 ✅ 熟悉了常用的线程API ✅ 了解了线程安全工具的选择 ✅ 攻克了高频面试题

下一步行动建议

记住:多线程编程是理论与实践相结合的技能,多写多练才能真正掌握!

🌟 如果觉得有帮助,建议收藏本文,面试前快速回顾!