简体   繁体   English

Java:while循环不会退出

[英]Java: While loop does not exit

Does any one have any idea why this while look ONLY exits IF theres a System.out.println()? 有谁知道为什么只有在出现System.out.println()的情况下才会退出?

The very same code doesn't work if I comment out the println() 如果我将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. 但是,当拼图完成时,如果没有println(),则不会显示完成,而对于println(),则将退出循环并退出程序。

To re-emphasize, everything works fine if theres a println() inside the while loop. 重新强调一下,如果while循环中有一个println(),那么一切都会正常工作。 When i comment it out it doesnt work. 当我注释掉它不起作用。

What you have is a tight CPU intensive loop that is repeatedly calling hasWon() . 您所拥有的是一个密集的CPU密集循环,该循环反复调用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. 看起来问题是紧张的CPU循环导致另一个线程饿死,并且插入println足以允许线程调度程序重新调度。

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. 就像@chrylis指出的那样,这样的繁忙循环效率极低。 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). 一个有问题的解决方案是将Thread.sleep()调用放入循环中(而不是println调用)。

  • A better solution is to use Object.wait() and Object.notify() . 更好的解决方案是使用Object.wait()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. 主线程在每次检查后在互斥对象上调用wait() ,而正在执行更新的线程每次在其互斥对象上执行可能会导致“获胜”的更新时,都会在同一互斥对象上调用notify()

The second solution also deals with the synchronization issue. 第二种解决方案还处理同步问题。 The wait() and notify() methods can only be performed while holding the mutex; wait()notify()方法只能在保持互斥锁的同时执行。 ie in a synchronized block or method. 即以synchronized块或方法。


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; 如果要强制计算机在每次通过循环时都重新检查它,则需要声明变量volatile 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. 相反,如果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. 最好还是保留一下winConds。

So the best solution would be removing the loop and on changing the grid, increment/decrement a counter, and on winning exit there, 因此,最好的解决方案是删除循环并更改网格,增加/减少计数器,并在那里赢得出口,

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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