V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jeffh
V2EX  ›  程序员

不明白, JDK 源码中大量的死循环为什么没导致 CPU 100%,而我写一个 while(true)一下子就 CPU 跑满了。

  •  2
     
  •   jeffh · 2019-11-22 10:31:31 +08:00 · 9835 次点击
    这是一个创建于 1827 天前的主题,其中的信息可能已经有所发展或是发生改变。
    困惑了许久的问题,google 了没找到满意的答案。
    第 1 条附言  ·  2019-11-22 12:14:24 +08:00

    大家别激动,稍安勿躁。

    阻塞的问题我也了解。另外先不纠结CPU多核的问题,while(true)长时间占满一个核我也是不同意的。while(true) sleep(1)我也了解,这里不说。

    我的疑问是JDK中的延时线程池ScheduledThreadPoolExecutor,假设现在我设定了一个task需要到今晚零点执行,那底层是否应该就死循环,来判断当前时间是否是零点。

    JDK中ScheduledThreadPoolExecutor源码如下,有for(;;)死循环:

    public RunnableScheduledFuture<?> take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                RunnableScheduledFuture<?> first = queue[0];
                if (first == null)
                    available.await();
                else {
                    long delay = first.getDelay(NANOSECONDS);
                    if (delay <= 0)
                        return finishPoll(first);
                    first = null; // don't retain ref while waiting
                    if (leader != null)
                        available.await();
                    else {
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            available.awaitNanos(delay);
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            if (leader == null && queue[0] != null)
                available.signal();
            lock.unlock();
        }
    }
    
    第 2 条附言  ·  2019-11-22 15:55:00 +08:00
    说操作系统时间片也别 bb 了,就说一句,while(true)也会切片。如果你设计一个定时任务框架,你怎么做,不要说什么挂个系统时间回调。
    第 3 条附言  ·  2019-11-22 16:51:38 +08:00
    感谢 @mccreefei #39 的回复,问题已解决。

    延时线程会计算目标时间到当前时间的毫秒数 delay,然后使用 awaitNanos(delay)挂起线程,明白了。此贴终结。
    45 条回复    2019-11-22 18:12:37 +08:00
    ZSeptember
        1
    ZSeptember  
       2019-11-22 10:55:20 +08:00
    代码呢。
    W1angMh
        2
    W1angMh  
       2019-11-22 11:05:30 +08:00
    代码贴出来啊兄弟
    Adlered
        3
    Adlered  
       2019-11-22 11:11:58 +08:00
    没代码说毛呢
    luckyrayyy
        4
    luckyrayyy  
       2019-11-22 11:14:42 +08:00
    jdk 源码大量的死循环???
    ivechan
        5
    ivechan  
       2019-11-22 11:16:59 +08:00
    2333,你该不会说得是类似下面这种死循环嘛?

    while(true) {
    recv();
    }
    daozhihun
        6
    daozhihun  
       2019-11-22 11:17:31 +08:00
    你确定 jdk 的死循环的线程不是在阻塞状态吗
    cxtrinityy
        7
    cxtrinityy  
       2019-11-22 11:20:02 +08:00 via Android
    先问是不是
    LuckyBoyGirl
        8
    LuckyBoyGirl  
       2019-11-22 11:23:59 +08:00
    你这个问题就类似 为何消费者也是用的 while(true) cpu 缺没有跑满 哈哈
    wutiantong
        9
    wutiantong  
       2019-11-22 11:28:22 +08:00
    这种问题自己留着琢磨多好。
    lhx2008
        10
    lhx2008  
       2019-11-22 11:29:52 +08:00 via Android
    JDK 里面的死循环一般里面都带阻塞的
    tabris17
        11
    tabris17  
       2019-11-22 11:31:01 +08:00
    while(true) sleep(1)

    你给我跑满 cpu 试试
    guolaopi
        12
    guolaopi  
       2019-11-22 11:31:19 +08:00
    1.跟 debug 有关
    2.人家的死循环是迫不得已的为了实现某种机制吧,比如 socket,代码中肯定包含终止循环的方法
    3.不贴代码让人怎么猜
    henices
        13
    henices  
       2019-11-22 11:34:02 +08:00
    现在的 CPU 多是多核,一般情况只能将某个核给跑满,对整个系统影响其实不大。
    ExploreWay
        14
    ExploreWay  
       2019-11-22 11:34:23 +08:00   ❤️ 1
    轮询超时机制限制,一般都会有吧!
    zivyou
        15
    zivyou  
       2019-11-22 11:35:28 +08:00
    循环体中有让线程挂起的地方
    ffkjjj
        16
    ffkjjj  
       2019-11-22 11:37:49 +08:00
    首先, JDK 里面一般都是阻塞的
    其次, 你试一下以下代码 cpu 占用的区别
    while(true){
    }

    while(true){
    try {
    Thread.sleep(100L);
    } catch (InterruptedException e) {
    return;
    }
    }
    sheep3
        17
    sheep3  
       2019-11-22 11:44:51 +08:00
    死循环一直算肯定跑满,你说的肯定有阻塞
    dosmlp
        18
    dosmlp  
       2019-11-22 11:55:00 +08:00   ❤️ 2
    说告诉你死循环=cpu 占用 100%的!!!??
    XiLemon
        19
    XiLemon  
       2019-11-22 12:00:03 +08:00 via iPhone
    说的是不是 cas 操作相关的代码
    jeffh
        20
    jeffh  
    OP
       2019-11-22 12:16:28 +08:00
    @XiLemon #19 cas 还好,比较虽然死循环但是总有成功的一次,不会长期占用 cpu 资源
    exip
        21
    exip  
       2019-11-22 12:17:06 +08:00 via Android
    人家那是有退出条件的死循环,你这是无条件的死循环.
    JingW
        22
    JingW  
       2019-11-22 12:21:02 +08:00
    你 await 一下你也不占 CPU
    XiLemon
        23
    XiLemon  
       2019-11-22 13:05:23 +08:00
    @jeffh #20 你这个代码贴的,里面不是有阻塞的方法么。。。
    ipwx
        24
    ipwx  
       2019-11-22 13:07:35 +08:00 via Android
    这种典型就是操作系统原理没学过才会发出的疑问。科班还是有科班的底蕴的。
    ipwx
        25
    ipwx  
       2019-11-22 13:11:01 +08:00 via Android   ❤️ 3
    这么说吧,线程占用 cpu 必须是在运行,而线程被操作系统调度才会被运行。因为 io 或者其他原因阻塞,线程会进入操作系统的等待队列,不会被运行。线程即使不进入阻塞,也不一定一直运行,操作系统随时可以打断线程,让它暂停,让渡资源给别的线程运行一段时间,再切换回来。当然资源充足情况下操作系统一般不会主动打断线程运行。
    opengps
        26
    opengps  
       2019-11-22 13:21:49 +08:00 via Android   ❤️ 3
    死循环不可怕,可怕的是死循环内部不休息
    misaka19000
        27
    misaka19000  
       2019-11-22 13:26:47 +08:00
    楼主基础堪忧
    Raymon111111
        28
    Raymon111111  
       2019-11-22 13:29:54 +08:00
    死循环但并不占用 cpu

    不知道这么讲能不能明白
    watzds
        29
    watzds  
       2019-11-22 13:30:36 +08:00 via Android
    贻笑大方
    php01
        31
    php01  
       2019-11-22 13:51:06 +08:00
    @ipwx 科班的就是厉害,要我来描述我就只能蹦出 4 个字:时间切片了,我可说不出这么长一串来
    enenaaa
        32
    enenaaa  
       2019-11-22 13:56:39 +08:00
    楼主三年经验看不懂这个代码不应该啊。
    axwz88
        33
    axwz88  
       2019-11-22 14:34:19 +08:00 via Android
    楼主你这问题也太没水平了吧。。。
    onice
        34
    onice  
       2019-11-22 15:19:52 +08:00
    以前我在项目中实现生产和消费也出现过这个问题,最大的原因就在于没有用阻塞方法。
    realpg
        35
    realpg  
       2019-11-22 15:33:09 +08:00   ❤️ 1
    我觉得楼上的所有人对死循环的理解都有问题……
    那种能自身条件打断的不叫死循环吧……
    mxT52CRuqR6o5
        36
    mxT52CRuqR6o5  
       2019-11-22 15:36:18 +08:00 via Android
    这不是被 try 包着,有异常就结束循环了,叫哪门子的死循环
    jeffh
        37
    jeffh  
    OP
       2019-11-22 15:47:48 +08:00 via Android
    @php01 这我就不同意了,难道 while(true)时间就不切片了?我是科班出身,可能没大家牛 b 吧。
    jeffh
        38
    jeffh  
    OP
       2019-11-22 16:13:05 +08:00
    @mxT52CRuqR6o5 #36 老哥,不能这么想啊,有异常就结束循环,如果人家程序就没异常呢。
    mccreefei
        39
    mccreefei  
       2019-11-22 16:15:55 +08:00
    设定了一个 task 需要到今晚零点执行,不是会计算 delay,然后 awaitNanos(delay)。没有任务也会 await(),这里死循环怎么就 cpu100%了?
    meik2333
        40
    meik2333  
       2019-11-22 16:22:52 +08:00
    https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html#awaitNanos-long-

    我不会 Java,使用 `RunnableScheduledFuture await` 关键词从 Google 搜到了上面的链接。

    简单的猜测一下,你给出的代码和 `while(true) sleep(1)` 没有本质的区别,sleep 之后线程挂起,不再占用 CPU 资源,时间到了之后由操作系统通过信号唤醒。
    NeroKamin
        41
    NeroKamin  
       2019-11-22 16:28:39 +08:00
    那么多 await 看不到吗
    jeffh
        42
    jeffh  
    OP
       2019-11-22 16:47:38 +08:00
    @mccreefei 兄弟,说到点上了,理解了。
    johnniang
        43
    johnniang  
       2019-11-22 17:14:00 +08:00 via Android
    顺便说一下,时间片是动态的,根据进程运行情况不断调整,而且操作系统会尽可能让 CPU 忙起来(尽管死循环会造成 CPU 空转)。
    nicebird
        44
    nicebird  
       2019-11-22 17:37:25 +08:00
    阻塞了呗。
    sleep、锁、wait、poll 等操作,cpu 时间就切出去了。
    passerbytiny
        45
    passerbytiny  
       2019-11-22 18:12:37 +08:00
    @jeffh 感情你这是只认识 sleep,不认识 await。问问题急躁不是大问题——别人会一边骂一边回答,但问完了还不认错就有问题了——后面别人直接不鸟你。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   938 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 21:18 · PVG 05:18 · LAX 13:18 · JFK 16:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.