孤舟笔记 并发篇十八 为什么启动线程不能直接调用run()方法?调用两次start()又会怎样?这个设计藏着大智慧
文章目录一、先说结论run() 和 start() 的核心区别二、直接调用 run()根本没有新线程start() 源码做了什么三、调两次 start()直接报错四、正确姿势需要新线程就创建新对象五、Thread 的状态机为什么只能 start 一次run() vs start() 全景回答技巧与点评标准回答加分回答面试官点评个人网站新手写多线程最容易犯两个错一是直接调run()而不是start()二是同一个线程对象调两次start()。前者线程根本没启动后者直接抛异常。但你有没有想过为什么 Java 要这么设计搞懂了这两个问题你对线程生命周期的理解就跨过了会用的门槛。一、先说结论run() 和 start() 的核心区别维度start()run()作用启动新线程由 JVM 调用 run()在当前线程中执行 run() 方法体是否创建新线程✅ 是❌ 否调用次数只能调一次可以反复调重复调用抛 IllegalThreadStateException正常执行底层机制native start0() 创建 OS 线程普通方法调用一句话记住start() 是开工仪式run() 是工作内容——仪式只能搞一次但活可以反复干。二、直接调用 run()根本没有新线程ThreadtnewThread(()-{System.out.println(当前线程: Thread.currentThread().getName());});t.run();// 输出当前线程: main 在主线程执行的t.start();// 输出当前线程: Thread-0 这才是新线程调 run() 就像你叫了一个外卖但没下单没 start自己跑去店里吃了——外卖员新线程根本没出动。调 start() 就像你正式下了单外卖员出发送货——新线程启动了run() 会在新线程中被 JVM 自动调用。start() 源码做了什么publicsynchronizedvoidstart(){if(threadStatus!0)// 状态检查 thrownewIllegalThreadStateException();group.add(this);start0();// native 方法创建 OS 线程 }privatenativevoidstart0();// JVM 实现真正创建线程关键流程start() 调用 native start0() → JVM 创建操作系统线程 → 新线程执行 run()。直接调 run() 绕过了这整条链路只是普通方法调用。三、调两次 start()直接报错ThreadtnewThread(task);t.start();// 正常启动t.start();// IllegalThreadStateException 看看源码为什么报错publicsynchronizedvoidstart(){if(threadStatus!0)// 已经启动过threadStatus ! 0 thrownewIllegalThreadStateException();...}为什么不让调两次因为线程的生命周期是单向的NEW → RUNNABLE → (BLOCKED/WAITING/TIMED_WAITING) → TERMINATED线程一旦启动就回不到 NEW 状态。你没法让一个已经跑完的线程复活就像没法让烧完的蜡烛再点一次——你得拿一根新的。生活类比火箭发射。倒计时结束、火箭升空你不能对着同一个火箭再按一次发射按钮——要么它还在飞已经启动了要么已经烧完了TERMINATED。四、正确姿势需要新线程就创建新对象// ❌ 复用同一个 Thread 对象ThreadtnewThread(task);t.start();t.start();// 报错// ✅ 创建新的 Thread 对象newThread(task).start();newThread(task).start();// 两个新线程没问题 // ✅ 更好的方式用线程池ExecutorServicepoolExecutors.newFixedThreadPool(2);pool.submit(task);pool.submit(task);// 线程池自动复用线程 五、Thread 的状态机为什么只能 start 一次start() NEW ──────────────────→ RUNNABLE │ ┌──────────┼──────────┐ ↓ ↓ ↓ BLOCKED WAITING TIMED_WAITING │ │ │ └──────────┴──────────┘ │ ↓ TERMINATEDthreadStatus 值对应0 NEW还没 start其他值 已启动RUNNABLE/BLOCKED/WAITING/TERMINATED 等start() 检查的就是这个值非 0 直接拒绝。run() vs start() 全景run() vs start() 全景 start() ├── 作用启动新线程 ├── 底层native start0() → 创建 OS 线程 → JVM 回调 run() ├── 只能调用一次重复调用抛 IllegalThreadStateException └── 线程状态NEW → RUNNABLE run() ├── 作用在当前线程执行任务 ├── 底层普通方法调用无新线程 ├── 可以反复调用就是普通方法 └── 线程状态无变化 线程生命周期单向不可逆 NEW → RUNNABLE → TERMINATED ↕ BLOCKED / WAITING / TIMED_WAITING 口诀start 建线程调一次run 是内容可反复 线程生命周期单行道start 过了不回头。回答技巧与点评标准回答直接调用 run() 不会创建新线程只是在当前线程中执行 run() 方法体等同于普通方法调用。start() 才会调用 native start0() 创建操作系统线程由 JVM 在新线程中回调 run()。调用两次 start() 会抛 IllegalThreadStateException因为线程生命周期是单向的start() 内部会检查 threadStatus非 0即已启动过就抛异常。需要再次执行任务应该创建新的 Thread 对象或使用线程池。加分回答设计哲学线程状态的单向流转是操作系统的底层设计——OS 线程一旦终止其资源栈、寄存器等已被回收Java 不可能在 JVM 层面复活一个 OS 线程。IllegalThreadStateException 是 Java 对底层不可逆性的上层表达Runnable 的优势正是因为 Thread 不能重复 start才更应该用 Runnable——任务是可复用的Thread 对象是一次性的。把任务和执行载体分离既省资源又灵活线程池的设计动机线程池的核心价值就是线程复用——避免了反复创建销毁 OS 线程的开销。Worker 线程跑完一个任务不退出而是循环取下一个任务本质上是绕过了线程只能 start 一次的限制面试官点评这道题考的是你对线程生命周期和底层机制的理解。能说出run() 不创建新线程只是入门能讲清 start0() 的 native 调用、threadStatus 的状态检查、以及线程生命周期单向不可逆的设计原因才说明你真正理解了线程的本质。如果你还能引出线程池的复用设计面试官会认为你有全局视野。原文阅读内容有帮助点赞、收藏、关注三连评论区等你
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2574506.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!