作为一名 Java 开发工程师,你一定在实际开发中遇到过需要同时处理多个任务的场景,比如:并发处理请求、异步日志写入、定时任务、高并发数据处理、多用户访问等。在这些场景中,Java 多线程(Multithreading) 是你必须掌握的核心技能。
Java 从诞生之初就对多线程提供了强大的支持。随着 Java 5 引入 java.util.concurrent 包、Java 8 的 CompletableFuture、Java 9 的 Flow API 等,Java 的并发模型越来越强大和易用。
本文将带你全面掌握:
- Java 多线程的基本概念(线程、进程、并发、并行)
- 线程的创建与启动方式(Thread、Runnable、Callable)
- 线程生命周期与状态管理
- 线程同步与线程安全(synchronized、volatile、Lock)
- 线程通信(wait/notify、Condition)
- 线程池(ExecutorService、ThreadPoolExecutor)
- 并发工具类(CountDownLatch、CyclicBarrier、Semaphore)
- Future 与 CompletableFuture
- 多线程在实际项目中的应用场景
- 多线程常见误区与最佳实践
并通过丰富的代码示例和真实项目场景讲解,帮助你写出更高效、更安全、结构更清晰的 Java 多线程代码。
🧱 一、什么是多线程?
✅ 线程(Thread) 是什么?
线程是操作系统调度的最小单位,一个进程中可以有多个线程,它们共享进程的资源。
✅ 进程(Process) 是什么?
进程是程序的一次执行过程,拥有独立的内存空间。
✅ 并发(Concurrency) vs 并行(Parallelism)
| 对比项 | 并发 | 并行 |
|---|---|---|
| 定义 | 多个任务交替执行(时间片轮转) | 多个任务同时执行(多核 CPU) |
| 适用场景 | 单核 CPU、任务切换频繁 | 多核 CPU、计算密集型任务 |
🔍 二、线程的创建方式
✅ 方式1:继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
✅ 方式2:实现 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行中:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
✅ 方式3:使用 Callable 和 Future 获取返回值
public class MyCallable implements Callable<String> {
@Override
public String call() {
return "任务完成:" + Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
System.out.println(future.get());
executor.shutdown();
}
}
🧠 三、线程的生命周期与状态
线程有 6种状态,通过 Thread.State 枚举表示:
| 状态 | 描述 |
|---|---|
NEW | 线程已创建,尚未启动 |
RUNNABLE | 正在运行或等待 CPU 资源 |
BLOCKED | 等待获取锁(阻塞) |
WAITING | 无限期等待(如 Object.wait()) |
TIMED_WAITING | 有时间限制的等待(如 Thread.sleep()) |
TERMINATED | 线程已结束 |
🔁 四、线程同步与线程安全
✅ 1. synchronized 关键字
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
✅ 2. ReentrantLock(显式锁)
public class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
✅ 3. volatile 关键字(保证可见性)
private volatile boolean running = true;
public void stop() {
running = false;
}
🧩 五、线程通信机制
✅ 1. wait() / notify() / notifyAll()
synchronized (lock) {
while (conditionNotMet) {
lock.wait(); // 释放锁,等待通知
}
// 条件满足,继续执行
}
✅ 2. Condition(配合 ReentrantLock 使用)
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (conditionNotMet) {
condition.await();
}
condition.signal();
} finally {
lock.unlock();
}
🧪 六、线程池(ThreadPool)详解
线程池可以复用线程资源,减少频繁创建和销毁线程的开销。
✅ Java 提供的线程池(java.util.concurrent.Executors)
| 线程池类型 | 特点 |
|---|---|
newFixedThreadPool | 固定大小线程池 |
newCachedThreadPool | 缓存线程池,按需创建 |
newSingleThreadExecutor | 单线程池,保证顺序执行 |
newScheduledThreadPool | 支持定时任务的线程池 |
✅ 示例:使用线程池执行任务
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int task = i;
executor.execute(() -> {
System.out.println("执行任务:" + task + ",线程:" + Thread.currentThread().getName());
});
}
executor.shutdown();
🧱 七、并发工具类
✅ 1. CountDownLatch(倒计时门闩)
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
latch.await(); // 等待所有线程完成
✅ 2. CyclicBarrier(循环屏障)
适用于多个线程相互等待,全部到达后再继续执行。
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程到达屏障");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
barrier.await();
}).start();
}
✅ 3. Semaphore(信号量)
用于控制同时访问的线程数量。
Semaphore semaphore = new Semaphore(2); // 同时允许2个线程访问
semaphore.acquire(); // 获取许可
// 执行操作
semaphore.release(); // 释放许可
🧩 八、Future 与 CompletableFuture
✅ Future:获取异步任务的结果
Future<String> future = executor.submit(() -> "任务完成");
String result = future.get(); // 阻塞直到任务完成
✅ CompletableFuture:链式异步编程(Java 8+)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 异步任务
return "结果";
});
future.thenApply(result -> result + "处理完成")
.thenAccept(System.out::println);
🧪 九、多线程在实际项目中的应用场景
场景1:并发处理订单(电商系统)
List<Order> orders = getOrders();
ExecutorService executor = Executors.newFixedThreadPool(10);
orders.forEach(order -> executor.submit(() -> processOrder(order)));
executor.shutdown();
场景2:定时任务调度(如日志清理、缓存刷新)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
cleanLogs();
}, 0, 1, TimeUnit.DAYS);
场景3:异步发送邮件(Spring Boot)
@Async
public void sendEmail(String to, String content) {
// 发送邮件逻辑
}
场景4:并行计算(如大数据处理)
List<Integer> data = getData();
int sum = data.parallelStream().mapToInt(Integer::intValue).sum();
🚫 十、常见误区与注意事项
| 误区 | 正确做法 |
|---|---|
| 忽略线程安全 | 使用同步机制(synchronized、Lock) |
| 不关闭线程池 | 使用 executor.shutdown() |
| 不处理异常 | 捕获异常或使用 UncaughtExceptionHandler |
| 不限制线程数量 | 使用线程池控制并发数 |
直接使用 Thread.sleep() 控制顺序 | 使用 CountDownLatch 或 CyclicBarrier |
| 不使用线程本地变量 | 使用 ThreadLocal 隔离线程数据 |
| 不使用并发工具类 | 使用 java.util.concurrent 包中的工具 |
| 不考虑线程复用 | 使用线程池复用线程 |
| 忽略 CPU 密集型任务 | 使用 ForkJoinPool 或 parallelStream |
| 不使用异步编程 | 使用 CompletableFuture 提高响应性 |
📊 十一、总结:Java 多线程核心知识点一览表
| 内容 | 说明 |
|---|---|
| 线程创建方式 | 继承 Thread、实现 Runnable、Callable |
| 线程状态 | NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED |
| 线程同步 | synchronized、ReentrantLock、volatile |
| 线程通信 | wait/notify、Condition |
| 线程池 | FixedThreadPool、CachedThreadPool、ScheduledThreadPool |
| 并发工具类 | CountDownLatch、CyclicBarrier、Semaphore |
| 异步编程 | Future、CompletableFuture |
| 实际应用 | 订单处理、定时任务、异步邮件、并行计算 |
| 最佳实践 | 使用线程池、显式同步、处理异常、避免死锁 |
📎 十二、附录:Java 多线程常用技巧速查表
| 技巧 | 示例 |
|---|---|
| 获取当前线程名 | Thread.currentThread().getName() |
| 设置线程优先级 | thread.setPriority(Thread.MAX_PRIORITY) |
| 设置守护线程 | thread.setDaemon(true) |
| 线程休眠 | Thread.sleep(1000) |
| 中断线程 | thread.interrupt() |
| 获取线程组 | Thread.currentThread().getThreadGroup() |
| 使用 ThreadLocal | private static ThreadLocal<User> userLocal = new ThreadLocal<>(); |
| 使用 ForkJoinPool | ForkJoinPool pool = new ForkJoinPool(); |
| 使用并行流 | list.parallelStream().forEach(...) |
| 设置线程名称 | new Thread(runnable, "MyThread") |
如果你正在准备一篇面向初学者的技术博客,或者希望系统回顾 Java 多线程编程的核心知识与实战技巧,这篇文章将为你提供完整的知识体系和实用的编程技巧。
欢迎点赞、收藏、转发,也欢迎留言交流你在实际项目中遇到的多线程相关问题。我们下期再见 👋
📌 关注我,获取更多Java核心技术深度解析!