Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第 8 周:多线程与线程池

本周目标

本周解决“同时做多件事”的问题。完成后,应能够:

  • 说明进程与线程、并发与并行的区别
  • 使用三种方式创建线程,并使用 Callable 获取返回值
  • 复现线程安全问题并说明产生原因
  • 使用同步代码块、Lock 和原子类保证线程安全
  • 使用线程池管理线程并说明核心参数的作用
  • 使用定时任务处理周期性工作

学习资料与范围

学习模块学习结果
线程创建与生命周期能够使用三种方式创建并启动线程
线程安全与同步能够复现并修复共享数据错误
Lock 与线程通信能够使用锁和等待唤醒机制
Callable、Future 与 FutureTask能够获取线程执行结果
线程池与定时器能够使用线程池和定时任务
并发基础概念能够说明原子性、可见性、有序性

需要掌握的知识点

线程基础

  • 进程、线程、并发、并行的区别
  • 创建线程的三种方式:继承 Thread、实现 Runnable、实现 Callable
  • start() 与 run() 的区别
  • 线程生命周期:新建、就绪、运行、阻塞、结束
  • Thread 常用方法:sleep、join、setName、setDaemon、setPriority
  • 线程的优先级和名称在调试中的作用
  • 主线程与子线程的执行顺序不可假设

线程安全

  • 共享可变数据是线程安全问题的根源
  • 复现问题:多线程对同一变量累加,结果少于预期
  • 同步代码块、同步方法和锁对象的作用
  • synchronized 的锁对象选择:不要锁字符串常量,尽量使用 final 对象
  • Lock 接口与 ReentrantLock 的基本用法,lock() 与 unlock() 的配对
  • 死锁的产生条件和避免方式:固定加锁顺序、减少锁范围
  • 原子类 AtomicInteger 的作用和使用场景
  • 原子性、可见性、有序性的基本含义
  • volatile 能解决什么、不能解决什么
  • 等待唤醒机制(wait/notify)的基本思路,了解生产者消费者模型

线程池与定时器

  • 线程池的作用:复用线程、控制并发数量、统一管理任务
  • ThreadPoolExecutor 的核心参数:核心线程数、最大线程数、空闲存活时间、任务队列、拒绝策略
  • 为什么推荐手动创建线程池而不是无脑使用 Executors
  • execute() 与 submit() 的区别
  • Callable 与 Future:提交任务、获取结果、处理异常、超时控制
  • 定时器:Timer 的局限与 ScheduledExecutorService 的用法
  • 线程池的关闭:shutdown() 与 shutdownNow()
  • 并发集合 ConcurrentHashMap 的使用场景(了解)

必做练习

  1. 三种创建方式:分别使用继承 Thread、实现 Runnable、Lambda 创建并启动线程,输出线程名称。
  2. 执行顺序:启动 3 个线程分别打印 1-5,观察每次运行结果顺序不同并解释原因。
  3. 线程安全复现:开启 10 个线程对同一变量累加 1000 次,输出实际结果并说明为什么小于 10000。
  4. 三种修复方案:分别使用同步代码块、ReentrantLock 和 AtomicInteger 修复上一题,并对比耗时。
  5. 死锁复现:编写两个线程互相等待对方的锁,复现死锁并说明预防方式。
  6. 带返回值的任务:使用 Callable 与 Future 让 3 个线程分别统计一部分成绩,再汇总结果。
  7. 线程池基础:手动创建线程池,提交 20 个任务并输出执行顺序和线程名称。
  8. 参数实验:修改核心线程数和队列长度,记录任务排队和执行情况的变化。
  9. 定时任务:使用 ScheduledExecutorService 每 2 秒输出一次统计信息,运行 10 秒后关闭。
  10. 并发集合:在多线程环境下分别向 HashMap 和 ConcurrentHashMap 写入数据,记录现象差异。
  11. 安全测试:为并发统计方法编写测试,重复运行多次,确认结果稳定。

本周综合任务:并发批量导入与导出

为项目增加批量数据导入导出功能,并使用多线程加速。

要求:

  • 支持从文本文件批量导入学生数据,数据量大时分成若干批次处理
  • 使用线程池并发处理批次,批次大小和线程数量可配置
  • 使用 Future 汇总各批次的成功条数和失败条数,最终输出导入报告
  • 对导入失败的记录输出具体原因(学号重复、成绩非法、格式错误)
  • 保证并发写入集合时的线程安全,说明采用的方案和理由
  • 使用定时任务输出一次导入进度,任务结束后正确关闭线程池
  • 为批量导入编写测试,验证结果与单线程导入一致
  • 记录一次线程安全问题的复现过程和修复对比

本周产出

  • 线程创建、线程安全和线程池练习代码
  • 并发批量导入导出功能及导入报告
  • 线程安全问题复现与修复记录
  • 线程池参数实验记录
  • 一份多线程与线程池笔记(含参数含义和常见坑)

完成检查

  • 能够使用三种方式创建线程并说明区别
  • 能够复现线程安全问题并解释原因
  • 能够使用同步代码块、Lock 或原子类修复线程安全问题
  • 能够说明死锁的产生条件和避免方式
  • 能够使用 Callable 和 Future 获取线程执行结果
  • 能够手动创建线程池并说明核心参数含义
  • 能够使用定时任务并在结束时关闭线程池
  • 已完成并发批量导入功能,结果与单线程一致