for(;;)和while(true)的区别

  Java   7分钟   306浏览   0评论

你好呀,我是小邹。

今天跟大家分享一下Java中的两种死循环写法:for(; ; )和while(true)

前言

在阅读Java的JDK源码时,发现大部分写源码的大佬多采用for(;;)的方式来死循环,比如说AQS(AbstractQueuedSynchronizer)中大量使用的自旋的方式获取共享状态。

源码:

/**
 * 通过“死循环”的方式来正确的添加节点
 */
private Node enq(final Node node) {
    // 不断循环,直至CAS插入节点成功
    for (;;) {
        Node t = tail;
        if (t == null) { 
            // 当尾节点为null,此时需要初始化头节点和尾节点
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 插入节点前驱节点指向原先尾节点
            node.prev = t;
            // CAS插入至同步队列的尾节点
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
/**
 * “死循环”获取同步状态,并且当前仅当前驱节点是头节点是才能够尝试获取同步状态
 */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 不断循环
        for (;;) {
            // 获取当前节点的前驱节点,如果前驱节点为null将会抛出空指针异常
            final Node p = node.predecessor();
            // 如果当前节点的前驱节点是头节点,尝试获取同步状态
            if (p == head && tryAcquire(arg)) {
                // 设置当前节点为头节点,并且将节点线程和节点的前驱节点置为null,help GC
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 如果不符合条件,则判断当前节点前驱节点的waitStatus状态来决定是否需要挂起LockSupport.park(this);
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        // 失败则取消
        if (failed)
            cancelAcquire(node);
    }
}

比较

Java代码在编译后都会转换为虚拟机可以识别的字节码,我们通过编译器对两者生成的字节码从原理是来观察两者的区别

测试代码for

package top.hqxiaozou.loop;

/**
 * @author:邹祥发
 * @date:2023/2/1 14:33
 */
public class ForTest {
    private static void forTest() {
        for (; ; ) {
        }
    }
}

通过 javap -v ForTest.class查看生成的字节码(只截取关键部分)

测试代码while

package top.hqxiaozou.loop;

/**
 * @author:邹祥发
 * @date:2023/2/1 14:49
 */
public class WhileTest {
    private static void whileTest() {
        while (true) {
        }
    }
}

通过 javap -v WhileTest.class查看生成的字节码(只截取关键部分)

在这里插入图片描述

总结

for死循环和while死循环编译后的字节码(编译器是可以做优化的),完全一模一样,所以两者在使用过程中,其实是没有任何区别。看到这里是不是有点生气,但是又想问问为什么源码那些大佬写代码基本上不用while(true),我想主要原因还是早期C语言中for(;;)循环和while(1)编译生成的字节码不一样,for(;;)生成的字节码明显更加少,一定程度上能节省一些内存空间。所以很多java大佬,也是精通各种其他语言的,因此写法习惯也就延续下来了吧。再者,我在查阅资料的时候也看到有笔者验证早期的Java编译器对for死循环编译生成的字节码也是少于while死循环编译后生成的字节码,可能随着编译器优化能力不断的增强,现在这两者在目前广泛使用的编译器中已经没有什么区别了。

如果你觉得文章对你有帮助,那就请作者喝杯咖啡吧☕
微信
支付宝
  0 条评论