Java 线程与 Runnable
Thread 与 Runnable 如何启动并发任务:线程生命周期入口、start/run 区别与基本退出意识。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java 线程与 Runnable
这个概念为什么出现
服务要同时处理多个请求、后台刷新缓存、异步发消息。Java 的经典并发起点是 线程(Thread) 与任务抽象 Runnable/Callable。先分清“任务”和“执行线程”,后面的线程池才有意义。
[!abstract] 一句话理解
Runnable描述要做的事;Thread.start()才会真正启动新线程并发执行,直接调用run()只是普通方法调用。
最小可运行示例
场景:下单后异步写审计日志,不阻塞主请求线程
public class AuditAsync {
static void handleOrder(String orderId) throws InterruptedException {
// 主线程:快速返回业务结果
System.out.println("accepted " + orderId + " on " + Thread.currentThread().getName());
Thread audit = new Thread(() -> {
// 审计任务:模拟写日志
System.out.println("audit " + orderId + " on " + Thread.currentThread().getName());
}, "audit-worker");
audit.start(); // 启动并发,而不是 audit.run()
audit.join(); // 演示中等待结束;真实服务常用线程池且不在请求线程 join
}
public static void main(String[] args) throws Exception {
handleOrder("ORD-1");
}
}
建议运行:
javac AuditAsync.java && java AuditAsync
期望输出(线程名可能略有差异,但 audit 行应出现):
accepted ORD-1 on main
audit ORD-1 on audit-worker
结合场景再看三个关注点
start才创建新并发执行。- 任务里未捕获的异常默认不会回到 main。
- 生产应优先线程池,避免无限制
new Thread。
核心概念与准确模型
1. 线程 vs 任务
- 任务:
Runnable/Callable - 执行者:
Thread或Executor
2. 生命周期直觉
NEW → RUNNABLE → 阻塞/等待 → TERMINATED(简化)
3. 中断
interrupt协作式取消- 阻塞调用抛
InterruptedException时应恢复中断状态或退出
4. 守护线程
setDaemon(true)不阻止 JVM 退出——勿用于必须完成的事务。
边界情况与反直觉行为
- 共享可变状态无同步 → 数据竞争(见后文)。
join死等导致请求线程卡死。- 线程名对排障极重要。
常见误区
[!warning] 常见误区:用 Thread.sleep 当同步协议 sleep 不能保证可见性/事件顺序;用锁、并发工具或 join/future。
工程实践
- 业务代码面向 Executor。
- 命名线程。
- 设置未捕获异常处理器。
- 明确取消与超时策略。
本节总结
- Runnable 是任务,Thread 是载体
- start ≠ run
- 为线程池与同步打基础
自测题
- 为什么不要在生产循环里无限 new Thread?
- 捕获 InterruptedException 后应怎样处理中断状态?
参考答案
- 无法统一限流、复用与关闭,易耗尽资源。
- 重新设置中断标志或结束任务,避免吞掉取消信号。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Thread (API) | API | 线程 |
| Concurrency (Oracle Tutorial) | 教程 | 并发入门 |