简体   繁体   中英

How to wait for an event to happen before continuing the current thread?

I am writing a GUI game, and I want to wait for the game to finish, before returning the winner. However, since my Game manager class is separate from the class used to implement the game logic and detect a win, I want to know how I can make the game manager wait until the other class notifies it of a win, and then proceed to do other stuff. I have not really worked much with multithreading before, so I'm hoping someone could explain how I might achieve this.
Here is some relevant code from the GameManager class' main that I've tried:

...
Game game = new Game(mode);
String winner = game.getWinner();
...

And from the Game class:

public Game(Game.Mode mode){
    this.board = new Board(7);
    window = new BoardFrame(7, 7, mode, board); //Graphical interface
    window.setVisible(true);
}

public String getWinner(){
    synchronized(board.WINNER){
        try {
            board.WINNER.wait();
        } catch (InterruptedException e) {}
    }
    return board.WINNER;
}

And the Board class:

public boolean update(int num){

    if (isValidMove(CurrentPlayer, num)){
        buttonClients.get(positions.get(CurrentPlayer)).clear();

        positions.put(CurrentPlayer, num);
        board[num] = 1;
        buttonClients.get(num).performCallback(CurrentPlayer);

        if ((WINNER = isGameFinished())!=""){
            //Disable all further inputs
            for (int i = 0; i<board.length; i++){
                board[i] = 1;
            }
            synchronized (WINNER) {
                WINNER.notify();
            }                                   
        }

        CurrentPlayer = (CurrentPlayer==PLAYER1)?PLAYER2:PLAYER1;
        return true;        
    }
    return false;
}

EDIT: So I have found the cause of the screen display issue, which is that all my code for the GameManager was in an EventQueue.invokeLater() block. I have taken it out of that block for now, and the screen is displaying properly now. However, when I play until the end, and the synchronized(WINNER) block is finally run, it seems like nothing happens? In other words, the game continues waiting on WINNER .

When I'm waiting on a thread, my favorite is using a CountDownLatch and Maybe using a Callable<V> and submitting it to a thread executor which returns a Future<V> which blocks until the thread is done.

public class Example {
    ExecutorService executorService = Executors.newCachedThreadPool();
    public void init(){
        CountDownLatch latch = new CountDownLatch(1);
        Future<Boolean> submit = executorService.submit(new Thread3());
        executorService.execute(new Thread1(latch, submit));
        executorService.execute(new Thread2(latch));
    }

    public class Thread1 implements Runnable{
        private CountDownLatch latch;
        private Future<Boolean> thread3;
        public Thread1(CountDownLatch latch, Future<Boolean> thread3) {
            this.latch = latch;
            this.thread3 = thread3;
        }

        @Override
        public void run() {
            int i = 0;
            try {
                thread3.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            while (i < 50){
                LockSupport.parkNanos(1000);
                i++;
            }
            latch.countDown();
        }
    }

    public class Thread2 implements Runnable{
        private CountDownLatch latch;

        public Thread2(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                latch.await();
                System.out.println("We can continue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public class Thread3 implements Callable<Boolean>{

        @Override
        public Boolean call() throws Exception {
            int i = 0;
            while (i < 50){
                LockSupport.parkNanos(1000);
                i++;
            }
            return true;
        }
    }

}

Good Resource: http://tutorials.jenkov.com/java-concurrency/index.html

There's a couple of ways you could do this, but by far the neatest will be to use the observer pattern to create an event listener. The idea behind this pattern is to allow code to register an action to be performed when an event occurs, while isolating the event handler from the action it's going to invoke. Note that this solution doesn't require any multithreading.

If you really do want your GUI and game logic running on separate threads, seems like you're trying to implement the locking approach described here . There's a couple of things to watch out for:

  • The JVM can give you a waiting thread an InterruptedException at any time, for any reason. You should always check the condition that caused you to wait before you proceed.
  • Any time you're accessing state shared across threads, you need to make sure you are using synchronized and volatile appropriately.
  • Where you lock on WINNER , bear in mind this field can change. If Board is locked on WINNER and changes its value, a new thread entering Game would be free to obtain its lock before the Board block completes, because they're locking on different objects.

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