作为一名 Java 开发工程师,你一定在实际开发中遇到过需要同时处理多个任务的场景,比如:并发处理请求、异步日志写入、定时任务、高并发数据处理、多用户访问等。在这些场景中,Java 多线程(Multithreading) 是你必须掌握的核心技能。
Java 从诞生之初就对多线程提供了强大的支持。随着 Java 5 引入 java.util.concurrent
包、Java 8 的 CompletableFuture
、Java 9 的 Flow API
等,Java 的并发模型越来越强大和易用。
本文将带你全面掌握:
并通过丰富的代码示例和真实项目场景讲解,帮助你写出更高效、更安全、结构更清晰的 Java 多线程代码。
线程是操作系统调度的最小单位,一个进程中可以有多个线程,它们共享进程的资源。
进程是程序的一次执行过程,拥有独立的内存空间。
对比项 | 并发 | 并行 |
---|---|---|
定义 | 多个任务交替执行(时间片轮转) | 多个任务同时执行(多核 CPU) |
适用场景 | 单核 CPU、任务切换频繁 | 多核 CPU、计算密集型任务 |
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();
}
}
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();
}
}
Callable
和 Future
获取返回值public class MyCallable implements Callable {
@Override
public String call() {
return "任务完成:" + Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future 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 |
线程已结束 |
synchronized
关键字public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
ReentrantLock
(显式锁)public class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
volatile
关键字(保证可见性)private volatile boolean running = true;
public void stop() {
running = false;
}
wait()
/ notify()
/ notifyAll()
synchronized (lock) {
while (conditionNotMet) {
lock.wait(); // 释放锁,等待通知
}
// 条件满足,继续执行
}
Condition
(配合 ReentrantLock
使用)Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (conditionNotMet) {
condition.await();
}
condition.signal();
} finally {
lock.unlock();
}
线程池可以复用线程资源,减少频繁创建和销毁线程的开销。
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();
CountDownLatch
(倒计时门闩)CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
latch.await(); // 等待所有线程完成
CyclicBarrier
(循环屏障)适用于多个线程相互等待,全部到达后再继续执行。
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程到达屏障");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
barrier.await();
}).start();
}
Semaphore
(信号量)用于控制同时访问的线程数量。
Semaphore semaphore = new Semaphore(2); // 同时允许2个线程访问
semaphore.acquire(); // 获取许可
// 执行操作
semaphore.release(); // 释放许可
Future
:获取异步任务的结果Future future = executor.submit(() -> "任务完成");
String result = future.get(); // 阻塞直到任务完成
CompletableFuture
:链式异步编程(Java 8+)CompletableFuture future = CompletableFuture.supplyAsync(() -> {
// 异步任务
return "结果";
});
future.thenApply(result -> result + "处理完成")
.thenAccept(System.out::println);
List orders = getOrders();
ExecutorService executor = Executors.newFixedThreadPool(10);
orders.forEach(order -> executor.submit(() -> processOrder(order)));
executor.shutdown();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
cleanLogs();
}, 0, 1, TimeUnit.DAYS);
@Async
public void sendEmail(String to, String content) {
// 发送邮件逻辑
}
List 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 提高响应性 |
内容 | 说明 |
---|---|
线程创建方式 | 继承 Thread、实现 Runnable、Callable |
线程状态 | NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED |
线程同步 | synchronized、ReentrantLock、volatile |
线程通信 | wait/notify、Condition |
线程池 | FixedThreadPool、CachedThreadPool、ScheduledThreadPool |
并发工具类 | CountDownLatch、CyclicBarrier、Semaphore |
异步编程 | Future、CompletableFuture |
实际应用 | 订单处理、定时任务、异步邮件、并行计算 |
最佳实践 | 使用线程池、显式同步、处理异常、避免死锁 |
技巧 | 示例 |
---|---|
获取当前线程名 | Thread.currentThread().getName() |
设置线程优先级 | thread.setPriority(Thread.MAX_PRIORITY) |
设置守护线程 | thread.setDaemon(true) |
线程休眠 | Thread.sleep(1000) |
中断线程 | thread.interrupt() |
获取线程组 | Thread.currentThread().getThreadGroup() |
使用 ThreadLocal | private static ThreadLocal |
使用 ForkJoinPool | ForkJoinPool pool = new ForkJoinPool(); |
使用并行流 | list.parallelStream().forEach(...) |
设置线程名称 | new Thread(runnable, "MyThread") |
如果你希望系统回顾 Java 多线程编程的核心知识与实战技巧,这篇文章将为你提供完整的知识体系和实用的编程技巧。
欢迎点赞、收藏、转发,也欢迎留言交流你在实际项目中遇到的多线程相关问题。我们下期再见
关注我,获取更多Java核心技术深度解析!