Прерывание потока Java изящно - PullRequest
4 голосов
/ 04 февраля 2012

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

public void run() {
    init();
    while (shouldIRun) {            
        updateGameState();
        checkUserInput();
        updateGameScreen(getGraphics());
        this.flushGraphics();         
    }
}

Прекрасно.Этот поток просто запускается и работает до тех пор, пока я не захочу его убить, когда я просто установил логическое значение shouldIRun в значение false, после чего он корректно завершается.

Но позже я понял, что хотел большего.Игра представляет собой игру-головоломку, и игрок может сделать неправильные шаги, а затем застрять.Когда это происходит, они могут запустить форму и выбрать опцию «уровень перезапуска».Затем устанавливается флаг restartLevel, и когда бесконечный цикл попадает в метод updateGameState(), уровень перезапускается.Но мне кажется, что это немного азартно - я не хочу начинать менять переменные объектов, используемых в основном цикле, в случае проблем с параллелизмом, хотя я, вероятно, параноик.На практике то, что я понял, что я хотел сделать, было очень ясно: я просто хотел приостановить поток бесконечного цикла, изменить переменные на то, что я хотел, и затем перезапустить.

Я сделалэто следующим образом:

public void run() {
    init();
    while (shouldIRun) {            

        if (shouldIWait) {
            iAmWaiting=true;
            while (shouldIWait) { };
            iAmWaiting=false;
        }

        updateGameState();
        checkUserInput();
        updateGameScreen(getGraphics());
        this.flushGraphics();
    }
}

Я думаю следующее.Если я теперь хочу «приостановить» этот второй поток из «базового» потока, я просто устанавливаю переменную shouldIWait в значение true, а затем просто зацикливаюсь, пока не обнаружу, что переменная iAmWaiting также является истинной.Теперь я точно знаю, что второй поток приостановился, и я точно знаю, где он остановился, где под словом «приостановлено» я на самом деле подразумеваю «застрял в бесконечном цикле».Теперь я могу бездельничать с некоторыми существенными переменными, перезапустить уровень и, в общем, разобраться, а затем, наконец, установить shouldIWait обратно в false, и мы снова пойдем.

У меня такой вопрос: это работает нормально, для меня, но попахивает тем, что является клуджем.Есть ли какой-то совершенно стандартный способ сделать то, что, по-видимому, является обычным делом - приостановить поток в данной точке и затем перезапустить его , когда я буду готов , что лучше, чем чтоЯ делаю?В частности, я подозреваю, что «положить Java в бесконечный цикл», возможно, не очень умная вещь.

Ответы [ 2 ]

1 голос
/ 04 февраля 2012

Может быть, было бы проще уничтожить поток и начать новый с нового уровня.

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

Я не думаю, что то, что вы в настоящее время делаете с занятым ожиданием, является злом.Как упоминал Бен Флинн в комментариях, вы могли бы сделать его немного занятым ожиданием, перебирая Thread.sleep (50).

1 голос
/ 04 февраля 2012

Обычно, это то, что вы бы использовали Object.wait() и Object.notify() для.

Есть несколько способов реализовать это в вашей ситуации, но вот простой пример:

Object monitor = new Object();
volatile boolean done = false, wait = false;

/* Running on one thread: */
public void run() {
    synchronized(monitor) {
        while(!done) {
            while(wait) {
                monitor.wait();
            }
            gameLogicAndStuff();
        }
    }
}

/* Running on another thread: */
public void showResetForm() {
    wait = true;
    synchronized(monitor) {
        actuallyShowResetForm();
        wait = false;
        monitor.notifyAll();
    }
}
...