【问题标题】:while(true) loop or java.util.Timer for a standard program loop?标准程序循环的 while(true) 循环或 java.util.Timer?
【发布时间】:2016-09-01 03:43:56
【问题描述】:

最近我做了一个非常基本的程序。具有暂停功能的计时器。它使用 3 个线程,2 个用于 Swing,1 个用于主线程。

对于这个程序,主线程中应该有一个增量时间计数部分。我做了一个非常基本的系统;

while(true)
{
    long now = System.currentTimeMillis();

    if(!sessionPaused)
    {
        if(now-programLastMs>1000)
        {
            save();
            programLastMs = now;
        }
        sessionMs += now-sessionPrevMs;
        overallMs += now-sessionPrevMs;

        sessionPrevMs = now;
        sessionLabel.setText(formatMillis("This Session:<br/>",sessionMs));
        overallLabel.setText(formatMillis("Overall:<br/>", overallMs));
    }
}

以上代码导致 CPU 使用率过高。然后我将该代码块替换为:

timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

                long now = System.currentTimeMillis();

                if(!sessionPaused)
                {
                    if(now-programLastMs>1000)
                    {
                        save();
                        programLastMs = now;
                    }

                    sessionMs += now-sessionPrevMs;
                    overallMs += now-sessionPrevMs;

                    sessionPrevMs = now;

                    sessionLabel.setText(formatMillis("This Session:<br/>",sessionMs));
                    overallLabel.setText(formatMillis("Overall:<br/>", overallMs));
                }
            }
        }, 0, 1);

问题就解决了。我只是想知道其中的原因。还有什么是创建程序循环的最佳方式?

【问题讨论】:

  • 您的while(true) 循环没有任何暂停,因此循环不断运行并不断消耗CPU。调度使您的 run 方法仅在调度时执行。
  • 所以如果我让调度程序每 50 纳秒调用一次,会不会一样?还有什么是创建程序循环的最佳方式? @Berger
  • 这取决于你的run方法的持续时间,但是50纳秒真的很低,你需要这么高的频率吗?然而,正如您所见,最好让您的应用程序呼吸,所以要么使用调度程序,要么将 Thread.sleep 添加到您的 while 循环中。
  • 你也不应该阻塞 Swing 线程。如果您在事件处理方法中执行长/无限循环,您的程序将无法重绘。请阅读concurrency in swing
  • 所以基本上 Thread.sleep(1) 在 while(true) 循环中与 Timer 有相同的效果吗? @Berger

标签: java multithreading loops timer


【解决方案1】:

您的第一个代码块基本上以 CPU 的速度运行。要理解这一点,您需要知道 IPSFLOPS 是什么。 现代 CPU 执行几个 GFLOPS,这意味着您的第一个块每秒执行数万次甚至数十万次,具体取决于您的硬件。如果你在主线程上运行它,它会阻塞你的 UI(即你的 GUI 会卡住)。在单独的线程上,它会持续运行并占用资源,而实际上并没有做太多事情。

另一方面,第二个块有特定的指令,每毫秒执行一次。这意味着你的代码块被执行了 1000 次,剩下的 cpu 时间被释放用于其他工作。

解决您评论中的问题:使用无限循环几乎是不可接受的。循环最适合用于一组指令的有界重复。在您的示例中,您似乎想每 1000 毫秒调用一次 save 并计算会话时间。我会

  1. 将保存分离到每 1000 毫秒调用一次的重复计时器。
  2. 仅在暂停状态更改时创建 onSessionPause 和 onSessionResume。这可能是由于暂停按钮或您定义的其他某种条件造成的。 onSessionPause 和 onSessionResume 函数应该能够记录它们被调用的时间,并根据时间差更新 sessionM。

除非您期望每毫秒都发生变化,否则您就是在让 CPU 做不必要的工作。

【讨论】:

  • 这是我一直在寻找的完美答案。感谢您提供详细信息(Y)
【解决方案2】:

还有什么是创建程序循环的最佳方式?

在您的具体情况下,最佳方式是每秒运行一次循环,因为这样可以以最少的 CPU 使用率实现您的目标。由于这是无法保证的,因此您需要找到一个足够低的阈值,以便您的循环至少每秒执行一次,并且不要太低,以提高效率。我建议您使用一些输出进行测试。

但请注意,不同的代码有不同的要求,因此没有通用的最佳方式来循环代码。作为基本规则,您应该尽量避免重复执行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 2014-08-21
    • 2016-04-24
    • 2021-04-02
    相关资源
    最近更新 更多