Java 中实现线程的创建和启动
在 Java 中,实现线程的创建和启动是两个不同但紧密相关的概念。理解为什么要启动线程(调用 start()
方法)而非直接调用 run()
方法,是掌握多线程编程的关键。以下是详细解释:
1. 线程的生命周期
Java 线程有明确的生命周期状态:
- 新建状态(New):创建
Thread
对象后(如new MyThread()
),线程尚未启动 - 可运行状态(Runnable):调用
start()
后,线程进入就绪队列 - 运行状态(Running):线程获得 CPU 时间片,执行
run()
方法 - 终止状态(Terminated):
run()
执行完毕
关键点:只有调用 start()
才能使线程从 New
进入 Runnable
状态!
2. start()
vs run()
的本质区别
方法 | 工作方式 | 线程数量 | 执行位置 |
---|---|---|---|
start() | JVM 创建新线程并执行 run() | 新线程 | 独立调用栈 |
run() | 直接调用普通方法 | 当前线程 | 当前线程调用栈 |
错误示例:
Thread thread = new Thread(() -> System.out.println("执行中"));
thread.run(); // 错误!在主线程同步执行
正确示例:
thread.start(); // 正确!启动新线程异步执行
3. 为什么必须通过 start()
启动线程?
(1) 资源分配
调用 start()
时,JVM 会:
- 为线程分配独立调用栈(Stack)
- 注册线程到系统调度器
- 触发操作系统级别的线程创建
(2) 异步执行
start()
使任务在后台异步执行,不阻塞当前线程- 直接调用
run()
会同步执行,阻塞当前线程
(3) 线程调度控制
只有通过 start()
启动的线程才能被:
- 线程调度器管理(优先级、时间片分配)
- 正确响应中断(
interrupt()
) - 加入线程池统一管理
(4) 状态合规性
多次调用 start()
会抛出 IllegalThreadStateException
,而 run()
可重复调用。这保证了线程状态机的正确性。
4. 底层机制
当调用 start()
时:
public synchronized void start() {if (threadStatus != 0) // 检查状态是否为NEWthrow new IllegalThreadStateException();group.add(this); // 加入线程组boolean started = false;try {start0(); // 关键!调用本地方法started = true;} finally {// ...错误处理}
}private native void start0(); // JVM实现的本地方法
start0()
是native
方法,由 JVM 通过操作系统 API 创建真实线程- 新线程创建后自动执行
run()
方法
5. 实际应用场景
假设需要同时下载3个文件:
// 错误方式(顺序下载)
new DownloadTask("url1").run(); // 阻塞主线程
new DownloadTask("url2").run(); // 等待前一个完成
new DownloadTask("url3").run();// 正确方式(并行下载)
new Thread(new DownloadTask("url1")).start(); // 异步
new Thread(new DownloadTask("url2")).start(); // 异步
new Thread(new DownloadTask("url3")).start(); // 异步
结论
操作 | 结果 | 是否启动线程 |
---|---|---|
new Thread() | 创建线程对象(NEW状态) | ❌ |
start() | 启动线程(进入RUNNABLE状态) | ✅ |
run() | 普通方法调用 | ❌ |
必须调用 start()
才能:
- 创建真正的操作系统线程
- 实现任务异步执行
- 符合线程生命周期规范
- 利用多核CPU实现并行计算
直接调用 run()
只是普通方法调用,完全违背了多线程的设计目的!