Есть ли у кого-нибудь какие-то идеи, почему это пока выглядит ТОЛЬКО, ВЫХОДИТ, ЕСЛИ это 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. Когда я прокомментирую это, он не работает.
У вас есть жесткий цикл интенсивного процессора, который неоднократно вызывает 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();
}
Очевидно, использование статики только для иллюстративных целей.
У вас есть цикл busy-wait, который постоянно проверяется.
Чтобы облегчить это, вы можете:
Thread,sleep(100L);
вместо этого, если System.out.prinntln.
Это не чистое решение.
Кстати: вместо этого
hasWon = hasWon && (board[i][z].getStatus() == winCond);
это немного быстрее
if (board[i][z].getStatus() != winCond) {
return false;
}
Лучше по-прежнему держать счетчик winConds.
Таким образом, лучшим решением было бы удаление цикла и изменение сетки, увеличение/уменьшение счетчика и выигрышный выход там,
Тот факт, что он работает сейчас, является несчастным случаем. Вам необходимо объявить переменную volatile
если вы хотите заставить компьютер перепроверить его на каждом проходе через цикл; в противном случае он мог бы просто прочитать значение один раз и больше не смотреть на него.
Кроме того, наличие цикла ожидания "ожидание", как это, ужасно неэффективно. Вместо этого, если вы хотите выйти на выигрыш, используйте код, который устанавливает этот флаг, в первую очередь, вызывая код выключения напрямую.