Run thread in background in java

2019-09-07 21:57发布

问题:

I'm writing a chess program in java. So far things are coming along fine but I do have a problem with updating my UI.

Here's a snippet of code from class ChessBoard which extends JPanel. This is called when a user tries to make a move:

if ( isLegalMove( aMove ) ) { // If the move's legal
    makeMove( aMove ); // Make that move
    select = null; // Reset some info
    drag = null;
    toggleTurn(); // Change turns
    generateMoves( 0 ); // Get legal moves for CPU
    repaint(); // Redraw board
    thread.run(); // Run chess algorithm
}

The thread is calling "run" on my instance of ChessBoard. The algorithm that finds the move can take several seconds to decide on a move.

I would like for my UI to update to reflect the user's move and then run the algorithm. That's why I run the algorithm on a separate thread. But my UI is not being updated until the computer also makes a move.

So if the user clicks a space to send a piece there, the screen freezes and then all of a sudden the piece has moved but the computer has moved also and it is again the player's turn.

Any help will be greatly appreciated.

回答1:

thread.run() is going to execute the code in the thread's run method on the current thread. You want thread.start().

Relevant JavaDoc

The repaint method doesn't actually repaint immediately. It basically tells the JPanel that it ought to repaint itself soon. Then you go ahead on the same thread and calculate the AI's move, which will freeze the window because Swing isn't multi-threaded.



回答2:

First, threads are not re-entrant (I'll explain that in a moment).

thread.run() is not causing the thread to execute in a separate thread, it's just call the run method of the thread (within the current Threads context.

What you need to do is set up a condition loop within your Thread that you can trigger in order to execute the logic you need.

public class ChessThread extends Thread { // I prefer Runnable, that's me
    protected static final Object NEXT_MOVE_LOCK = Object();

    public ChessThread() {
        setDaemon(true); // This will allow the JVM to exit without the need to terminate the thread...
    }

    public void doNextMove() {
        // Notify the "wait" that we want to continue calculating the next move
        synchronized (NEXT_MOVE_LOCK) {
            NEXT_MOVE_LOCK.notify();
        }
    }       

    public void run() { 
        while (true) {
            // Wait for the "next move" request
            synchronized (NEXT_MOVE_LOCK) {
                try {
                    NEXT_MOVE_LOCK.wait();
                } catch (InterruptedException exp) {
                }
            }
            // Calculate the next move...
        }
    }
}

Now, Threads are non-reentrant, this means that once the run method has complete, that instance of the Thread can not be restarted.

Hence using thread.start() more then once will not work (can't remember if it throws an exception or not) (hence the reason I prefer Runnable)

So. What you want to do, is start the Thread when your program loads and when you need to, call thread.doNextMove() to cause it calculate the what ever it is you need.

Now, also remember, Swing is not Thread safe. That is, you should NEVER update the UI from any Thread other than the Event Dispatching Thread (or EDT)

You might also want to have a read through Concurrency in Swing

Oh and Concurrency in Java