好的,我们来系统地复习 Java 线程相关的知识。线程是 Java 并发编程的核心,理解它对于编写高性能、响应式的应用程序至关重要。
第一部分:核心概念
1. 进程 vs 线程
进程:
- 操作系统资源分配的基本单位
- 每个进程有独立的内存空间(堆、栈、代码区)
- 进程间通信(IPC)比较复杂(如管道、信号、共享内存等)
- 创建和销毁开销较大
线程:
- CPU 调度的基本单位,是进程内的执行单元
- 同一进程的线程共享堆和方法区,但有独立的程序计数器和栈
- 线程间通信相对简单(共享内存)
- 创建和销毁开销较小
2. 线程的生命周期(状态)
Java 线程有 6 种状态:
- NEW(新建):线程被创建但尚未启动
- RUNNABLE(可运行):正在运行或准备运行
- BLOCKED(阻塞):等待监视器锁(synchronized)
- WAITING(等待):无限期等待其他线程的特定动作
- TIMED_WAITING(超时等待):有限时间等待
- TERMINATED(终止):线程执行完毕
3. 创建线程的三种方式
1. 继承 Thread 类
1 | class MyThread extends Thread { |
2. 实现 Runnable 接口(推荐)
1 | class MyRunnable implements Runnable { |
3. 实现 Callable 接口(可返回结果)
1 | class MyCallable implements Callable<String> { |
4. 线程同步与锁
关键问题:多个线程访问共享资源时可能出现竞态条件。
解决方案:
synchronized 关键字
- 同步方法
- 同步代码块
Lock 接口(更灵活)
ReentrantLock:可重入锁ReadWriteLock:读写锁
5. 线程通信
wait():让当前线程等待notify():唤醒一个等待的线程notifyAll():唤醒所有等待的线程
6. volatile 关键字
保证变量的可见性,但不保证原子性。
第二部分:实际应用场景
场景 1:多线程下载文件
1 | public class FileDownloader { |
场景 2:生产者-消费者模式
1 | public class ProducerConsumerExample { |
场景 3:使用线程池处理批量任务
1 | public class ThreadPoolExample { |
第三部分:常用的线程使用模式
1. 线程池的最佳实践
1 | // 推荐使用 ThreadPoolExecutor 自定义参数 |
2. 使用 CompletableFuture 进行异步编程
1 | public class CompletableFutureExample { |
3. 使用 volatile 保证可见性
1 | public class VolatileExample { |
4. 使用 Atomic 类保证原子性
1 | public class AtomicExample { |
重要注意事项
- 避免死锁:按固定顺序获取锁
- 避免线程泄漏:确保使用线程池并正确关闭
- 处理异常:在 Runnable 的 run 方法中捕获所有异常
- 资源清理:使用 try-finally 或 try-with-resources 确保资源释放
- 性能考虑:避免过度同步,尽量使用局部变量
总结
Java 线程编程的核心要点:
- 理解线程生命周期和状态转换
- 掌握同步机制:synchronized 和 Lock
- 熟练使用线程池管理线程资源
- 了解现代并发工具:CompletableFuture、Atomic 类
- 遵循最佳实践:避免死锁、正确处理异常
通过实际场景的练习,你可以更好地理解这些概念并在实际项目中正确应用它们。