while(true) 和 for(;;) 哪个性能更好?
结论:没有任何性能差别。两者都是无条件循环,javac 生成完全相同的字节码:循环体 + 一条 goto 回边,没有条件判断。while(true) 里的 true 是编译期常量,编译器直接省略条件检查;for(;;) 本身省略了条件表达式,语义等价于 true。选哪个只看可读性与团队习惯。
1. 字节码对比
public class Loop {
static void work() { }
static void a() { while (true) { work(); } }
static void b() { for (;;) { work(); } }
}
javap -c Loop 的结果(两个方法体完全一致):
static void a();
Code:
0: invokestatic #7 // Method work:()V
3: goto 0
static void b();
Code:
0: invokestatic #7 // Method work:()V
3: goto 0
对比带条件的循环,差别就出来了:
| 写法 | 是否生成条件判断 | 字节码特征 |
|---|---|---|
while (true) { ... } | 否 | 循环体 + goto,无 ifeq |
for (;;) { ... } | 否 | 循环体 + goto,无 ifeq |
while (flag) { ... } | 是 | iload flag; ifeq exit |
for (int i = 0; i < n; i++) | 是 | iload; iload; if_icmpge exit |
既然字节码相同,JIT 编译后的机器码也相同,分支预测、指令流水线都没有区别。所谓"for(;;) 更快"是 C 语言时代的遗留印象(早期某些 C 编译器对 while(1) 有额外告警或优化差异),在 Java 里不成立。
2. 语义与规范上的细微差别
| 维度 | while (true) | for (;;) |
|---|---|---|
| 条件表达式 | 常量 true,JLS 要求是 boolean | 缺省,规范等价于 true |
| 可读性 | 意图直白,新人友好 | 简短,常见于底层/老代码 |
| 编译器告警 | 部分检查工具提示"常量条件" | 一般不提示 |
| 典型用法 | 服务主循环、事件循环 | 底层代码、精简写法 |
| 代码风格检查 | 多数规范接受 | 部分团队禁止(可读性差) |
一个真实差异在泛型与 lambda 上下文:while (true) 里的 break 只能跳出循环;而 for (;;) 外层如果套了 switch,break 的语义要小心(break 只跳出 switch,不跳出 for)。这跟性能无关,但写错会死循环。
3. 代码:如何安全地写无限循环
public class Worker {
private volatile boolean running = true; // 必须 volatile,否则可能读到缓存值
public void run() {
while (running) { // 有退出条件,优先用这种
try {
Task t = queue.take();
t.execute();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断标志
break; // 退出循环
}
}
}
public void shutdown() { running = false; }
/** 纯自旋:等一个极短的条件,配 onSpinWait 降低功耗 */
static void spin(AtomicBoolean ready) {
while (!ready.get()) {
Thread.onSpinWait(); // JDK 9+,提示 CPU 进入自旋优化
}
}
}
三个要点:
- 别在
while(true)里靠sleep轮询:延迟不可控、退出只能靠标志位,还会把响应时间拖成秒级; - 退出标志要
volatile:否则工作线程可能永远读到旧值(JMM 可见性); - 纯自旋要加
Thread.onSpinWait()或LockSupport.parkNanos:空转会把 CPU 打满。
4. 常见追问
| 追问 | 回答要点 |
|---|---|
| 为什么字节码一样还会有人争论? | 语言习惯与历史印象,C 语言里曾有差异,Java 没有 |
| JIT 会优化掉空循环吗? | 通常会(循环无副作用时可被消除),但带 IO/同步的循环不会 |
死循环会导致 finally 不执行吗? | 会,控制流永远不离开 try |
while(true) 里 return 会执行 finally 吗? | 会,return 是正常出口 |
空转和 park 怎么选? | 等待时间极短(纳秒级)用自旋,否则用 park/Condition 阻塞 |
为什么 volatile 能保证退出? | 写操作对其他线程立即可见,读操作每次从主内存刷新 |
| CPU 飙高排查时如何区分? | top -Hp 找线程 → jstack 看栈,空转线程栈顶通常是循环方法 |
小结
while(true) 与 for(;;) 字节码一致、性能一致,属于"面试题里的伪问题"。真正要关心的是如何安全退出、如何避免空转、可见性怎么保证——这三点才是线上事故的来源。