Java - 为什么这个基本的计时类会占用如此多的CPU?

3
细节: 对于我开发的许多程序,我使用这个代码(或一些轻微的变体)来定期“打勾”一个方法,设置为变量tps(如果设置为32,则每秒调用方法tick 32次)。它非常重要,因此我无法从我的代码中删除它,因为动画和其他各种部分将会出问题。
不幸的是,由于某种原因,它似乎使用了相当可观的CPU使用率。前段时间,我考虑使用thread.sleep()来解决这个问题,但根据this post;的说法,它的精度不够高,因此不可行,因为这需要相当准确的时间控制。
它并没有使用那么多的cpu,在我的短暂测试中,一颗ryzen 1700的CPU使用率大约在6-11%之间,但考虑到它所做的很少,这仍然相当多。是否有 less cpu intensive 的方法来完成这个?还是时间上的精度对于常规使用来说太低了。
public class ThreadTest {

    public ThreadTest() {
        int tps = 32;

        boolean threadShouldRun = true;
        long lastTime = System.nanoTime();
        double ns = 1000000000 / tps;
        double delta = 0;
        long now;

        while (threadShouldRun) {
            now = System.nanoTime();

            delta += (now - lastTime) / ns;
            lastTime = now;

            while ((delta >= 1) && (threadShouldRun)) {
                tick();
                delta--;
            }
        }
    }

    public void tick() {

    }

    public static void main(String[] args) {
        new ThreadTest();
    }
}

基本概述:以上代码在使用Ryzen 1700时会占用6-11%的CPU,有没有一种方法在Java中完成相同的代码,并且在执行一定次数的代码时保持合理的时间,同时减少CPU的使用率。


8
由于线程不断运行且没有休眠或主动让出,虽然意图是提供高精度,但会导致线程过热(并持续消耗CPU周期)。 - MadProgrammer
一个单线程的无限循环会占用一个核心,还有什么更/少的你想要的呢?;) - javaPhobic
1
这是一个解决的问题,建议您考虑使用调度服务。但即使不使用调度服务,只需在紧密循环中添加 Thread.yield(),您也可能会注意到一些改进,而风险极小。 - Paul Hicks
2
@PaulHicks 在Java 9中,你现在也可以使用Thread.onSpinWait(); - Jacob G.
很酷,不知道这个。看起来onSpinWait()是专门用于需要发生其他事情的情况,所以在这种情况下,yield()可能是更具自我记录性的选项。 - Paul Hicks
显示剩余2条评论
1个回答

4
一个不会占用太多CPU的简单替代方法是使用ScheduledExecutorService。例如:
public static void main(String[] args) {
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    executor.scheduleAtFixedRate(() -> {

    }, 0, 31250, TimeUnit.MICROSECONDS);
}

请注意,31250代表将1/32秒转换为微秒的值,因为该参数接受一个long类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接