简体   繁体   中英

Java: While loop does not exit

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.

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; ie 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.

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.

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,

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM