摘要:在网上混迹多年,我的ID叫“尘民1024”。“尘民”取自《灵笼·灯塔》,而“1024”是程序员心中的无限可能。祝愿大家都能从“尘民”走向“上民”。
在网上混迹多年,我的ID叫“尘民1024”。“尘民”取自《灵笼·灯塔》,而“1024”是程序员心中的无限可能。祝愿大家都能从“尘民”走向“上民”。
面试官推了推眼镜,眼神锐利地盯着我:“Java 线程能不能多次调用 start方法?”
我心里一紧:谁会有病调两次 start呢?尽问些没用的。
犹豫了两秒,我只好硬着头皮说:“额……理论上,start方法只能调用一次,再调用会出错……”
面试官皱了皱眉:“还有呢?”
我支支吾吾:嗯……这个……我没仔细看过。---- #技术分享 随着空气突然安静下来……
面试官:“谢谢你的回答,面试到这里就结束了。”
同一个线程实例只能调用 1 次 start 方法,多次调用会抛出 IllegalThreadStateException 。
原因 :线程状态从 NEW 转为 RUNNABLE 后不可逆,重复调用会破坏线程生命周期管理。
public class StartTwiceDemo { public static void main(String args) { Thread t = new Thread( -> System.out.println("线程运行中..."));t.start; t.start; } }Exception in thread "main" java.lang.IllegalThreadStateException at java.lang.Thread.start(Thread.java:708) at com.soul.yd.bk.StartTwiceDemo.main(StartTwiceDemo.java:8)在 Thread 类的源码中,我们可以看到 start 方法内部会检查线程的状态,如果线程的状态不为 NEW (即未启动状态),就会抛出 IllegalThreadStateException 。这个判断 if (threadStatus != 0) 的关键作用在于确保线程只被启动一次,防止在启动后,线程状态变得不可控。
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) { } } }源码方法分析
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) { } } }private native void start0;具体来看,它防止了以下几个潜在的问题:
1. 线程的生命周期不可逆
线程在调用 start 方法后,会从 NEW 状态进入 RUNNABLE 状态,意味着它已经提交给操作系统调度。如果再次调用 start ,线程状态已发生变化,可能会导致:
重复调度 :操作系统误认为线程可以重新调度,浪费资源。并发问题 :多个线程试图执行相同任务,可能引发资源争用和同步问题。2. 线程状态的同步问题
start 方法的状态改变是原子操作,使用 synchronized 确保线程安全。如果没有这个判断,可能会发生:
线程状态交叉 :线程间状态更新不同步,导致错误的线程调度。竞态条件 :多个线程修改状态,造成不一致的执行流。3. 资源管理问题
线程启动涉及操作系统资源分配,如果重复启动线程,可能会导致:
资源浪费 :多次分配内存和 CPU 时间,影响性能。死锁/活锁 :重复启动的线程可能引发锁竞争,导致死锁或活锁。4. Java 的并发模型
Java 的 Thread 类要求每个线程独立,有自己独特的状态和堆栈。多次调用 start 会破坏这种独立性,导致:
线程竞争 :线程间的调度和状态管理混乱,可能出现不可预见的行为。来源:墨码行者