Как я могу запретить циклу Java while потреблять> 50% моего процессора?

Хорошо, я проверил это на пустой программе, и простое выполнение while (true) {} дало мне> 50% загрузки моего процессора. У меня есть игра, над которой я работаю, в которой в качестве основного цикла используется цикл while, а его процессор все время находится на уровне 100.

Как я могу заставить Java повторять что-то снова и снова, не потребляя> 50% моего процессора, просто чтобы повторять?

Ответов (9)

Решение

Добавьте сон, чтобы перевести поток в режим ожидания на некоторый интервал:

Thread.sleep

Без сна цикл while потребляет все доступные вычислительные ресурсы. (Например, теоретически 100% в одноядерной системе или 50% в двухъядерной и т. Д.)

Например, следующее будет проходить цикл один раз while примерно каждые 50 миллисекунд:

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

Это должно немного снизить загрузку процессора.

В режиме ожидания в цикле операционная система также будет предоставлять достаточно системного времени для других потоков и процессов, чтобы они могли выполнять свои функции, поэтому система также будет реагировать. Во времена одноядерных систем и операционных систем с не очень хорошими планировщиками такие циклы могли сделать систему очень невосприимчивой.


Поскольку while возникла тема использования циклов для игры, если игра будет включать графический интерфейс, игровой цикл должен быть в отдельном потоке, иначе сам графический интерфейс перестанет отвечать.

Если программа будет консольной игрой, то многопоточность не будет проблемой, но с графическими пользовательскими интерфейсами, управляемыми событиями, наличие долгоживущего цикла в коде приведет к тому, что графический интерфейс не будет отвечать.

Многопоточность и тому подобное - довольно сложные области программирования, особенно в начале, поэтому я предлагаю задать еще один вопрос, когда это станет необходимым.

Ниже приведен пример приложения Swing на основе, JFrame которое обновляет объект, JLabel который будет содержать возвращаемое значение из System.currentTimeMillis . Процесс обновления происходит в отдельном потоке, и кнопка «Стоп» останавливает поток обновления.

Этот пример проиллюстрирует несколько концепций:

  • Приложение GUI на основе Swing с отдельным потоком для обновления времени - это предотвратит блокировку потока GUI. (Вызывается EDT или потоком отправки событий в Swing.)
  • Наличие whileцикла с условием цикла, которого нет true, но замененным на, booleanкоторый будет определять, поддерживать ли цикл в рабочем состоянии.
  • Как 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:

Это связано с колебаниями , но идея остается той же: попробуйте использовать шаблон Observer вместо wile(true) .

Похоже, ваш поток занят ожиданием. Другими словами, он попал в цикл, в котором ничего не делает. В такой ситуации он безрезультатно прожигает много циклов процессора.

Как уже упоминалось другими, Thread.sleep - это ответ.

Обычно в игровом цикле вы делаете небольшую паузу ... например: http://developers.sun.com/mobility/midp/articles/game/

Thread.Sleep не может быть полным ответом. Для большинства игр слишком много процессора для необходимого объема работы. Простой сон в течение определенного времени не так уж и эффективен, поскольку вы, скорее всего, по-прежнему будете сжигать слишком много ресурсов или их недостаточно. Обычно вы хотите каким-то образом рассчитать время для своей работы.

Если задуматься, экран обновляется только с определенной скоростью, обычно менее 100 раз в секунду. Я не знаком с API Java для такого рода вещей, но вы хотите узнать скорость обновления монитора, а затем обновлять только один раз между каждым обновлением.

Кроме того, вам не нужен такой цикл для вашего основного цикла, вы можете использовать таймеры для вызова кода обновления через равные промежутки времени. Это даже более эффективно.

Вы действительно заняты ожиданием, что означает, что вы постоянно перегружаете свой процессор, проверяя одно или несколько условий, пока они не станут истинными.

Thread.sleep() это не решение. Использование wait() и notify() методов позволяет делать только то , что вы пытаетесь, но гораздо более эффективно. По сути, этот механизм позволяет потоку «спать» на объекте до тех пор, пока объект не решит, что что-то произошло, и не захочет разбудить все спящие на нем потоки.

Примеры кода можно найти здесь и здесь.

Это должно быть вашим решением, а не скрытием вашего занятого ожидания с таймером.

Как насчет планировщика на основе Quartz-Spring, который продолжает выполнять работу снова и снова с повторяющимися интервалами.

Хотя да, вы могли бы

Thread.sleep(50)

как следует из принятого ответа, вы также можете позвонить

Thread.sleep(0) 

Это скажет процессору выполнить переключение контекста. Затем будут выполнены другие потоки, ожидающие выполнения (например, поток рисования графического интерфейса пользователя), и машина перестанет чувствовать себя медленной.

Способ сна (0) также максимизирует время, предоставляемое ОС вашему приложению, потому что поток немедленно вернется в очередь процессора (вместо ожидания 50 мс перед этим), поэтому, если нет другого потока, ожидающего, ваш поток будет продолжен выполняется.

Если есть прерывание, то, вероятно, по какой-то причине. Лучший способ справиться с этим -

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

Однако этот цикл ничего не делает, поэтому я предлагаю вам просто удалить его. Довольно редко вы хотите быть занятым в ожидании условия (в этом случае класс Condition - лучший выбор), обычно вы можете изменить то же поведение, чтобы вообще не нуждаться в цикле.