while(true) 和 for(;;) 哪个性能更好?

#Java#面试八股

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 进入自旋优化
        }
    }
}

三个要点:

  1. 别在 while(true) 里靠 sleep 轮询:延迟不可控、退出只能靠标志位,还会把响应时间拖成秒级;
  2. 退出标志要 volatile:否则工作线程可能永远读到旧值(JMM 可见性);
  3. 纯自旋要加 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(;;) 字节码一致、性能一致,属于"面试题里的伪问题"。真正要关心的是如何安全退出、如何避免空转、可见性怎么保证——这三点才是线上事故的来源。

笔记加载中…