views:

3641

answers:

9

Okay, I tested this on an empty program, and just having a while(true){} running gave me >50% on my CPU. I have a game I'm working on that uses a while loop as it's main loop, and it's CPU is at 100 all the time.

How can I get Java to repeat something over and over without eating up >50% of my CPU just to do the repeating?

+21  A: 

Add a sleep to put the thread into idle for some interval:

Thread.sleep

Without having a sleep, the while loop will consume all the computing resources that is available. (For example, theoretically, 100% in a single core system, or 50% in a dual core, and so on.)

For example, the following will cycle once through a while loop approximately every 50 milliseconds:

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

This should bring down the CPU utilization quite a bit.

With a sleep in the loop, the operating system will also give enough system time for other threads and processes to do their things, so the system will be responsive as well. Back in the days of single core systems and operating systems with not-so-good schedulers, loops like this could have made the system very unresponsive.


Since the topic of use of while loops for a game came up, if the game is going to involve a GUI, the game loop must be in a separate thread or else the GUI itself will become unresponsive.

If the program is going to be a console-based game, then threading is not going to be an issue, but with graphical user interfaces which are event-driven, having a long-living loop in the code will make the GUI unresponsive.

Threading and such are pretty tricky areas of programming, especially when getting started, so I suggest that another question be raised when it becomes necessary.

The following is an example of a Swing application based in a JFrame which updates a JLabel that will contain the returned value from System.currentTimeMillis. The updating process takes place in a separate thread, and a "Stop" button will stop the update thread.

Few concepts the example will illustrate:

  • A Swing-based GUI application with a separate thread to update time -- This will prevent lock up of the GUI thread. (Called the EDT, or event dispatch thread in Swing.)
  • Having the while loop with a loop condition that is not true, but substituted with a boolean which will determine whether to keep the loop alive.
  • How Thread.sleep factors into an actual application.

Please excuse me for the long example:

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();
            }
        });
    }
}

Some resources about threading and using Swing:

coobird
That was my first thought, but you beat me to it :)
Pwninstein
That worked perfectly. Thank you!
William
Note that polling (sleeping for some amount of time) is perfectly fine for the example code (updating a text label with the current time). But for processing complex event queues, it's generally better to block on the presence of entries in the event queue (using a monitor)
Kevin Day
Thread.sleep(1); Would work just as well.
Gary Willoughby
Thread.sleep(0) too, forcing a context switch. See answer below.
Frederic Morin
+5  A: 

Usually in a game loop you will pause for a bit... for example: http://developers.sun.com/mobility/midp/articles/game/

TofuBeer
A: 

It seems like your thread is busywaiting. That is to say, it is trapped in a loop in which it does nothing. In such a situation, it will chew up a lot of cpu cycles to no effect.

As mentioned by others, Thread.sleep is the answer.

Rob Lachlan
+20  A: 

Thread.Sleep may not be the whole answer. For most games, there's way too much CPU for the amount of work needed. Simply sleeping for a set amount of time isn't all that efficient, since you will likely either still burn too many resources, or not enough. You typically want to time your work in some way.

If you think about it, the screen only updates at a certain rate, typically less than 100 times per second. I'm not familiar with the Java api's for this sort of thing, but what you want is to find out the refresh speed of the monitor and then update only a single time between each refresh.

Further, you don't need a loop like that for your main loop, you can use timers to call your update code at a regular interval. This is even more efficient.

Mystere Man
A: 

That's swing-related, but the idea stays the same: try to use Observer pattern instead of wile(true).

alex
+6  A: 

You are indeed busy-waiting, meaning you are grinding your CPU constantly on checking one or more conditions until they are true.

Thread.sleep() is not the solution. Using wait() and notify() methods allows you to do just what you are trying, but much more efficiently. Basically, this mechanism allows a thread to sleep on an object until the object decides something has happened and wants to wake all the threads sleeping on it.

Code exmaples can be found here and here.

This should be your solution, and not hiding your busy-wait with a timer.

Yuval A
Using java.util.concurrent is probably going to be better.
Tom Hawtin - tackline
+1, but I think a timer event is be better.
Software Monkey
A: 

How about a Quartz-Spring based scheduler that keeps doing the work over and over again in repeated intervals.

Franklin
+1  A: 

While yes, you could do a

Thread.sleep(50)

like the accepted answer suggest, you could also call

Thread.sleep(0)

This will tell the processor to do a context switch. Other threads waiting to be executed (like the GUI drawing thread) will then be executed and the machine will stop feeling slow.

The sleep(0) way will also maximise the time given by the OS to you application because the thread will immediatly go back in the processor's queue (instead of waiting 50ms before doing so) so if no other thread where waiting, you thread will continue being executed.

Frederic Morin
this is exactly what Thread.yield() does...
Yuval A
yield is not recommended because it's behavior is not standard depending on the platform. see http://www.javamex.com/tutorials/threads/yield.shtml
Frederic Morin
A: 

If there is an interrupt it is probably for a reason. A better way to handle this is

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

However this loop doesn't do anything, so I suggest you just delete it. It is fairly rare that you want to busy wait for a condition (In which case the Condition class is a better choice) usually you can transform the same behaviour not to need a loop at all.

Peter Lawrey