第一部分:线程创建方式
Java提供了多种创建线程的方式,适用于不同场景需求
学习目标
掌握4种线程创建方式:继承Thread类、实现Runnable接口、实现Callable接口、使用线程池
继承Thread类
通过继承Thread类并重写run()方法创建线程
注意:直接调用run()方法不会启动新线程,只会执行普通方法
实现Runnable接口
更常用的方式,避免单继承限制
实现Callable接口
带返回值的线程创建方式,需要配合FutureTask使用
使用线程池
推荐的生产环境用法,高效管理线程资源
创建方式对比
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
继承Thread | 简单直接 | 无法继承其他类 | 简单任务 |
实现Runnable | 解耦任务与线程,可继承其他类 | 无返回值 | 大多数场景 |
实现Callable | 支持返回值,可抛出异常 | 使用复杂 | 需要返回结果 |
线程池 | 高效资源管理,避免频繁创建销毁 | 配置复杂 | 生产环境 |
最佳实践建议
1. 优先选择实现Runnable接口方式创建线程,避免单继承限制
2. 需要返回值时使用Callable接口
3. 生产环境必须使用线程池管理线程资源
4. 避免直接创建Thread对象,防止资源耗尽
第二部分:线程生命周期
理解线程的6种状态及其转换关系
学习目标
掌握线程的6种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
新建
可运行
阻塞
等待
限时等待
终止
NEW(新建)
线程被创建但未启动时的状态
创建方式: new Thread()
转换条件: 调用start()方法进入RUNNABLE状态
RUNNABLE(可运行)
线程正在运行或准备运行的状态
包含: 就绪(Ready)和运行中(Running)
转换条件: 时间片用完或主动放弃进入BLOCKED/WAITING
BLOCKED(阻塞)
等待获取锁时的状态
触发条件: 进入synchronized方法/块
转换条件: 获取到锁后进入RUNNABLE状态
WAITING(等待)
无限期等待其他线程操作
触发方法: Object.wait(), Thread.join()
转换条件: 被notify()/notifyAll()唤醒
TIMED_WAITING(限时等待)
有限时间等待状态
触发方法: Thread.sleep(), Object.wait(timeout)
转换条件: 时间结束或被唤醒
TERMINATED(终止)
线程执行结束后的状态
触发条件: run()方法执行完成
转换条件: 不可再启动
状态监控技巧
1. 使用jstack工具查看线程状态
2. 在IDEA中通过调试视图查看线程状态
3. Thread.getState()方法获取状态
4. 避免线程长时间处于BLOCKED/WAITING状态
第三部分:线程常用方法
掌握线程控制的核心方法
学习目标
理解并掌握start(), run(), sleep(), join(), yield(), interrupt()等方法的使用
方法 | 描述 | 使用示例 | 注意事项 |
---|---|---|---|
start() | 启动线程,进入RUNNABLE状态 | thread.start() | 只能调用一次 |
run() | 线程执行的主体方法 | 重写此方法 | 不要直接调用 |
sleep(long millis) | 使线程休眠指定毫秒数 | Thread.sleep(1000) | 不释放锁 |
join() | 等待线程执行完毕 | thread.join() | 可能导致死锁 |
yield() | 让出CPU时间片 | Thread.yield() | 不保证立即切换 |
interrupt() | 中断线程 | thread.interrupt() | 需要配合异常处理 |
interrupt()方法详解
中断线程的正确方式:
重要注意事项
1. 不要使用stop()、suspend()等已废弃方法
2. 正确处理InterruptedException
3. volatile不能替代synchronized
4. sleep()和wait()的区别:sleep()不释放锁,wait()释放锁
第四部分:线程同步与锁
解决多线程并发访问共享资源的问题
学习目标
掌握synchronized关键字和Lock接口的使用,理解死锁产生原因及预防
synchronized关键字
Java内置同步机制,简单易用
优点: 自动释放锁,无需手动管理
缺点: 功能有限,无法中断等待锁的线程
Lock接口
更灵活的同步控制,需要手动释放锁
优点: 可中断、可尝试获取、公平锁等
缺点: 需要手动释放,使用不当可能导致死锁
死锁示例与避免
避免死锁策略
1. 按固定顺序获取锁资源
2. 使用tryLock()尝试获取锁
3. 设置超时时间
4. 使用死锁检测工具
死锁发生时两个线程互相等待对方释放资源
第五部分:常见错误与解决方案
多线程编程中的典型问题与调试技巧
1. 竞态条件(Race Condition)
现象: 多个线程同时修改共享数据,结果不确定
解决方案: 使用synchronized或Lock保证操作的原子性
2. 死锁(Deadlock)
现象: 多个线程互相等待对方释放资源,程序停滞
解决方案: 按顺序获取锁,设置超时,使用死锁检测工具
3. 活锁(Livelock)
现象: 线程不断响应对方动作,无法继续执行
解决方案: 引入随机性,使用重试退避策略
4. 线程饥饿(Starvation)
现象: 某些线程长时间无法获取资源
解决方案: 使用公平锁,调整线程优先级
5. 内存可见性问题
现象: 线程修改值后其他线程看不到更新
解决方案: 使用volatile关键字或synchronized保证可见性
6. 资源泄漏
现象: 线程未正确关闭,占用系统资源
解决方案: 使用try-finally确保资源释放,使用线程池管理
调试多线程程序的技巧
1. 使用Thread Dump分析线程状态
2. 利用日志记录线程ID和操作
3. 使用IDE的并发调试工具
4. 编写确定性测试用例
5. 限制并发数逐步排查问题
第六部分:练习与测验
通过练习巩固线程知识
动手练习
- 实现一个生产者-消费者模型
- 使用Callable和Future实现并行计算
- 编写一个死锁示例并解决它
- 使用线程池实现Web服务器请求处理
线程知识测验
学习资源推荐
1. 《Java并发编程实战》- Brian Goetz
2. 《Java并发编程之美》- 翟陆续
3. Oracle官方Java并发教程
4. Java并发工具包(java.util.concurrent)文档