20

好的,我在一个空程序上对此进行了测试,仅仅运行了一段时间(true){},我的 CPU 就超过了 50%。我有一个我正在开发的游戏,它使用一个 while 循环作为它的主循环,它的 CPU 一直是 100。

我怎样才能让 Java 一遍又一遍地重复某些事情,而不会仅仅为了重复而占用我 50% 以上的 CPU?

4

9 回答 9

44

添加睡眠以使线程空闲一段时间:

Thread.sleep

在没有睡眠的情况下,while 循环将消耗所有可用的计算资源。(比如理论上单核系统100%,双核50%,以此类推。)

例如,以下将while大约每 50 毫秒循环一次:

while (true)
{
    try
    {
        Thread.sleep(50);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

这应该会大大降低 CPU 利用率。

通过在循环中休眠,操作系统也会为其他线程和进程提供足够的系统时间来完成它们的工作,因此系统也会做出响应。回到单核系统和调度程序不太好的操作系统的时代,像这样的循环可能会使系统非常无响应。


由于出现while了游戏循环使用的话题,如果游戏要涉及 GUI,游戏循环必须在单独的线程中,否则 GUI 本身将变得无响应。

如果程序将是基于控制台的游戏,那么线程不会成为问题,但对于事件驱动的图形用户界面,代码中的长期循环将使 GUI 无响应。

线程等是非常棘手的编程领域,尤其是在开始时,所以我建议在必要时提出另一个问题。

下面是一个基于 a 的 Swing 应用程序示例,JFrame它更新JLabel将包含来自 的返回值的a System.currentTimeMillis。更新过程发生在一个单独的线程中,“停止”按钮将停止更新线程。

该示例将说明的几个概念:

  • 一个基于 Swing 的 GUI 应用程序,具有一个单独的线程来更新时间——这将防止 GUI 线程锁定。(在 Swing 中称为 EDT 或事件调度线程。)
  • 使while循环的循环条件不是true,而是用一个booleanwhich 代替,这将确定是否保持循环处于活动状态。
  • 如何Thread.sleep影响实际应用。

请原谅我的长例子:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TimeUpdate
{
    public void makeGUI()
    {
        final JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JLabel l = new JLabel();

        class UpdateThread implements Runnable
        {
            // Boolean used to keep the update loop alive.
            boolean running = true;

            public void run()
            {
                // Typically want to have a way to get out of
                // a loop. Setting running to false will 
                // stop the loop.
                while (running)
                {
                    try
                    {
                        l.setText("Time: " +
                                System.currentTimeMillis());

                        Thread.sleep(50);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }

                // Once the run method exits, this thread
                // will terminate.
            }
        }

        // Start a new time update thread.
        final UpdateThread t = new UpdateThread();
        new Thread(t).start();

        final JButton b = new JButton("Stop");
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e)
            {
                t.running = false;
            }
        });

        // Prepare the frame.
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(l, BorderLayout.CENTER);
        f.getContentPane().add(b, BorderLayout.SOUTH);
        f.setLocation(100, 100);
        f.pack();
        f.setVisible(true);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new TimeUpdate().makeGUI();
            }
        });
    }
}

关于线程和使用 Swing 的一些资源:

于 2009-02-24T03:57:21.887 回答
29

Thread.Sleep 可能不是全部答案。对于大多数游戏来说,CPU 太多了,无法满足所需的工作量。简单地睡一段时间并没有那么高效,因为您可能仍然会消耗太多资源,或者不够。您通常希望以某种方式为您的工作计时。

如果你仔细想想,屏幕只会以一定的速度更新,通常每秒不到 100 次。我不熟悉这类事情的 Java api,但你想要的是找出监视器的刷新速度,然后在每次刷新之间只更新一次。

此外,您的主循环不需要像这样的循环,您可以使用计时器定期调用更新代码。这甚至更有效。

于 2009-02-24T05:21:33.433 回答
12

您确实在忙于等待,这意味着您不断地检查 CPU 以检查一个或多个条件,直到它们为真。

Thread.sleep() 不是解决方案。使用wait()notify()方法可以让你做你正在尝试的事情,但效率更高。基本上,这种机制允许线程在对象上休眠,直到对象确定发生了某些事情并想要唤醒所有休眠在其上的线程。

可以在此处此处找到代码示例。

应该是您的解决方案,而不是用计时器隐藏您的忙碌等待。

于 2009-02-24T07:58:20.310 回答
7

是的,你可以做一个

Thread.sleep(50)

就像接受的答案建议的那样,您也可以致电

Thread.sleep(0) 

这将告诉处理器进行上下文切换。其他等待执行的线程(如 GUI 绘图线程)将被执行,机器将停止感觉缓慢。

sleep(0) 方式还将最大化操作系统为您的应用程序提供的时间,因为线程将立即返回处理器的队列(而不是在这样做之前等待 50 毫秒),因此如果没有其他线程在等待,您的线程将继续被执行。

于 2009-02-24T20:50:59.300 回答
6

通常在游戏循环中你会暂停一下......例如:http: //developers.sun.com/mobility/midp/articles/game/

于 2009-02-24T03:58:38.467 回答
0

看来您的线程正在忙着等待。也就是说,它被困在一个什么都不做的循环中。在这种情况下,它会消耗大量的 cpu 周期而没有效果。

正如其他人所提到的, Thread.sleep 就是答案。

于 2009-02-24T03:59:07.903 回答
0

这是与 swing 相关的,但想法保持不变:尝试使用观察者模式而不是wile(true).

于 2009-02-24T07:50:33.450 回答
0

一个基于 Quartz-Spring 的调度程序如何以重复的时间间隔一遍又一遍地完成工作。

于 2009-02-24T11:11:25.293 回答
0

如果有中断,可能是有原因的。处理此问题的更好方法是

try {
    while (true) {
        Thread.sleep(50);
    }
} catch (InterruptException e) {
    Thread.currentThread().interrupt();
}

但是这个循环没有做任何事情,所以我建议你删除它。您想忙于等待条件(在这种情况下,Condition 类是更好的选择)是相当罕见的,通常您可以转换相同的行为,根本不需要循环。

于 2009-02-28T19:44:43.200 回答