Java: цикл не завершается

1

Есть ли у кого-нибудь какие-то идеи, почему это пока выглядит ТОЛЬКО, ВЫХОДИТ, ЕСЛИ это System.out.println()?

Тот же самый код не работает, если я прокомментирую println()

do{

    System.out.println("");

    if(hasWon()){
        InOut.out(output() + "\nYou Won");
        System.exit(0);
    }
} while (!hasWon()) 

Программа выглядит следующим образом

static final int gridSize = Integer.parseInt(InOut.in(1, "Enter grid size"));
static Tile[][] board = new Tile[gridSize][gridSize];
static int winCond = 1;
static GuiFrame f = new GuiFrame(gridSize);
static BtnPanel p = new BtnPanel(gridSize);
static JButton[][] btn = new JButton[gridSize][gridSize];

public static void main(String[] args) {

    //Creating objects
    for (int i = 0; i < gridSize; i++) {
        for (int z = 0; z < gridSize; z++) {
            board[i][z] = new Tile();
        }
    }
    GUI();

    while (!hasWon()) {

        System.out.println("");

        if(hasWon()){
            InOut.out(output() + "\nYou Won");
            System.exit(0);
        }
    }
}

public static boolean hasWon() {
    boolean hasWon = true;

    for (int i = 0; i < gridSize; i++) {
        for (int z = 0; z < gridSize; z++) {
            hasWon = hasWon && (board[i][z].getStatus() == winCond);
        }
    }

    return hasWon;
}

public static String output() {
    String message = "";

    for (int i = 0; i < gridSize; i++) {
        for (int z = 0; z < gridSize; z++) {
            message += board[i][z].getStatus() + " ";
        }
        message += "\n";
    }

    return message;
}

public static void GUI() {

    for (int i = 0; i < gridSize; i++) {
        for (int z = 0; z < gridSize; z++) {
            String btnValue = "";
            btnValue += board[i][z].getStatus();
            btn[i][z] = new JButton();
            btn[i][z].setText(btnValue);
            btn[i][z].addActionListener(f);
            p.add(btn[i][z]);
        }
    }

    f.add(p, BorderLayout.CENTER);

}

public static void modifyGUI(int i, int z){
    btn[i][z].setText(String.valueOf(board[i][z].getStatus()));
}

Это игра-головоломка, в которой пользователь щелкает плитки и смежные плитки. Однако, когда головоломка завершена, без println() она не показывает завершение, а println() выходит из цикла и выходит из программы.

Чтобы снова подчеркнуть, все работает отлично, если theres println() внутри цикла while. Когда я прокомментирую это, он не работает.

  • 2
    Пожалуйста, покажите короткую, но полную программу, демонстрирующую проблему.
  • 7
    Где вы переключаете флаг "выиграл" с ложного на истинный? Я этого не вижу
Показать ещё 6 комментариев
Теги:

3 ответа

2
Лучший ответ

У вас есть жесткий цикл интенсивного процессора, который неоднократно вызывает hasWon(). Также похоже, что некоторые другие потоки отвечают за обновление состояния платы, что приводит к позиции "выиграл".

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

Другое возможное объяснение заключается в том, что у вас есть общая структура данных, которую один поток читает, а другой обновляет... без какой-либо синхронизации. Теоретически, тот факт, что вы не синхронизируете, может означать, что изменения, сделанные обновляющим потоком, никогда не видны нишу чтения, поскольку компилятор не видел необходимости вставлять "барьерные" инструкции, которые будут приводить к соответствующему очистке кеша и т.д.,


Как отмечает @chrylis, занятость, подобная этому, ужасно неэффективна. Что вам нужно сделать, это заменить цикл занятости:

  • Хакерное решение состоит в том, чтобы поместить Thread.sleep() в цикл (вместо вызова println).

  • Лучшим решением является использование Object.wait() и Object.notify(). Основной поток вызывает wait() для объекта mutex после каждой проверки, а поток, который выполняет обновление, вызывает notify() в том же мьютексе каждый раз, когда он выполняет обновление, которое может привести к позиции "выиграл".

Второе решение также касается проблемы синхронизации. Методы wait() и notify() могут выполняться только при удержании мьютекса; т.е. в synchronized блоке или методе.


Вот некоторые фрагменты кода, чтобы показать, как wait/notify можно реализовать.

    public static final Object mutex = new Object();
    public static boolean finished = false;

    // One thread
    synchronized (mutex) {
        while (!finished) {
             mutex.wait();
        }
    }

    // Another thread
    synchronized (mutex) {
        finished = true;
        mutex.notify();
    }

Очевидно, использование статики только для иллюстративных целей.

  • 0
    Так я должен добавить notify () в классе actionPerformed и wait (), заменяя цикл? Если так, что я называю «Объект»?
0

У вас есть цикл busy-wait, который постоянно проверяется.

Чтобы облегчить это, вы можете:

Thread,sleep(100L);

вместо этого, если System.out.prinntln.

Это не чистое решение.

Кстати: вместо этого

        hasWon = hasWon && (board[i][z].getStatus() == winCond);

это немного быстрее

        if (board[i][z].getStatus() != winCond) {
            return false;
        }

Лучше по-прежнему держать счетчик winConds.

Таким образом, лучшим решением было бы удаление цикла и изменение сетки, увеличение/уменьшение счетчика и выигрышный выход там,

0

Тот факт, что он работает сейчас, является несчастным случаем. Вам необходимо объявить переменную volatile если вы хотите заставить компьютер перепроверить его на каждом проходе через цикл; в противном случае он мог бы просто прочитать значение один раз и больше не смотреть на него.

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

  • 0
    Не совсем. Переменная будет считана из ближайшего возможного кэша (который, вероятно, является регистром процессора). Вам нужно сделать его изменчивым, если вы знаете, что кто-то другой может изменить его из какого-то другого потока. Я предполагаю, что System.out.println ("") каким-то образом форсировал ввод-вывод, который в какой-то момент сбрасывал регистры процессора.
  • 0
    Полностью согласен с занятой вращающейся частью, хотя
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню