在 Java 中,如果对同一个线程对象多次调用 start()
方法,会引发 java.lang.IllegalThreadStateException
异常。这是因为 start()
方法的设计规定,一个线程只能启动一次。
原理分析
-
线程的生命周期:
-
一个线程从创建到终止,经历以下几种状态:
- NEW(新建) :线程对象被创建,但还未调用
start()
方法。 - RUNNABLE(可运行) :调用
start()
方法后,线程进入就绪状态,等待 CPU 调度。 - TERMINATED(终止) :线程运行结束或异常退出后,进入终止状态。
- NEW(新建) :线程对象被创建,但还未调用
-
-
start()
方法的作用:start()
方法的功能是启动线程,将线程从NEW
状态变为RUNNABLE
状态。- 内部通过调用 JVM 的本地方法
start0()
来启动线程。 - 如果线程已经不是
NEW
状态,再次调用start()
会导致异常。
-
为什么禁止多次调用
start()
?start()
方法仅能将线程从NEW
状态变为RUNNABLE
。- 如果允许多次调用
start()
,线程生命周期的管理将变得复杂,尤其是在已经执行过的线程上重复调用时。
示例代码
java
public class ThreadStartExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running...");
});
thread.start(); // 第一次调用,线程启动成功
thread.start(); // 第二次调用,抛出 IllegalThreadStateException 异常
}
}
输出:
plaintext
Thread is running...
Exception in thread "main" java.lang.IllegalThreadStateException
at java.base/java.lang.Thread.start(Thread.java:834)
at ThreadStartExample.main(ThreadStartExample.java:10)
异常根本原因
-
在
Thread
类的start()
方法实现中,有一个状态检查:javaif (threadStatus != 0) throw new IllegalThreadStateException();
threadStatus
是线程的内部状态标志,只有在NEW
状态下,threadStatus
为 0。- 如果线程已经启动过(
RUNNABLE
、TERMINATED
等状态),再次调用start()
时状态不为 0,抛出异常。
start()
方法源码
以下是 JDK 8 中 Thread
类的 start()
方法核心源码:
java
public synchronized void start() {
if (threadStatus != 0) // 检查线程状态
throw new IllegalThreadStateException();
group.add(this); // 将线程添加到线程组中
boolean started = false;
try {
start0(); // 调用本地方法启动线程
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this); // 启动失败时移除线程
}
} catch (Throwable ignore) {
}
}
}
start0()
的底层实现
start0()
是一个本地方法,其底层逻辑由 JVM 的 C/C++ 代码实现。以下是 start0()
的部分行为:
3.1 线程创建(os::create_thread
)
-
调用操作系统 API 创建原生线程。
-
不同操作系统的实现有所不同:
- 在 Linux 系统中,使用
pthread_create
。 - 在 Windows 系统中,使用
_beginthreadex
。
- 在 Linux 系统中,使用
3.2 线程栈初始化
- 为新线程分配栈内存空间,确保运行时需要的资源就绪。
- 栈空间大小由 JVM 参数(如
-Xss
)控制。
3.3 调度线程执行
- 将新创建的线程交给操作系统进行调度。
- 当操作系统分配 CPU 时间片时,线程的
run()
方法会开始执行。
总结
- 多次调用
start()
:不被允许,会抛出IllegalThreadStateException
。 - 原因:线程生命周期状态的限制,一个线程对象只能被启动一次。
- 解决:如需重复执行任务,创建新的线程对象,或使用线程池(推荐)。