Reputation: 183
Does any one have any idea why this while look ONLY exits IF theres a System.out.println()?
The very same code doesn't work if I comment out the println()
do{
System.out.println("");
if(hasWon()){
InOut.out(output() + "\nYou Won");
System.exit(0);
}
} while (!hasWon())
The program is as follows
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()));
}
This is a puzzle game where the user clicks a tile and adjacent tiles change as well. However when the puzzle is completed, without a println() it does not show completion, with a println() it exits the loop and exits the program.
To re-emphasize, everything works fine if theres a println() inside the while loop. When i comment it out it doesnt work.
Upvotes: 2
Views: 2026
Reputation: 719709
What you have is a tight CPU intensive loop that is repeatedly calling hasWon()
. It also looks like some other thread is responsible for updating the board state that results in the "won" position.
It looks like problem is that the tight CPU loop is causing the other thread to starve, and inserting the println
is sufficient to allow the thread scheduler to reschedule.
Another possible explanation is that you have a shared data structure that one thread is reading and another is updating ... without any synchronization. In theory, the fact that you are not synchronizing could mean that changes made by the updating thread are never seen by the reading thread, because the compiler has not seen the need to insert "barrier" instructions that will cause the relevant cache flushing, etc.
As @chrylis notes, busy looping like that is horribly inefficient. What you need to do is replace the busy looping:
A hacky solution is to put a Thread.sleep()
call into the loop (instead of the println
call).
A better solution is to use Object.wait()
and Object.notify()
. The main thread calls wait()
on a mutex object after each check, and the thread that is doing the updating calls notify()
on the same mutex each time it does an update that might result in a "won" position.
The second solution also deals with the synchronization issue. The wait()
and notify()
methods can only be performed while holding the mutex; i.e. in a synchronized
block or method.
Here are some code snippets to show how wait / notify can be implemented.
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();
}
Obviously, the use of statics is just for illustrative purposes.
Upvotes: 4
Reputation: 109613
You have a busy-wait loop that constantly checks.
To alleviate you might do:
Thread,sleep(100L);
instead if the System.out.prinntln.
It is not a clean solution.
By the way: Instead if
hasWon = hasWon && (board[i][z].getStatus() == winCond);
this is a bit faster
if (board[i][z].getStatus() != winCond) {
return false;
}
Better still keep a counter of winConds.
So the best solution would be removing the loop and on changing the grid, increment/decrement a counter, and on winning exit there,
Upvotes: 0
Reputation: 77234
The fact that it works now is an accident. You need to declare your variable volatile
if you want to force the computer to recheck it on every pass through the loop; otherwise, it could just read the value once and never look at it again.
Additionally, having a busy-wait loop like that is horribly inefficient. Instead, if you want to exit on win, have the code that sets that flag in the first place call the shutdown code directly.
Upvotes: 0