【问题标题】:Thread waiting for user to click something线程等待用户点击某物
【发布时间】:2012-11-25 09:58:57
【问题描述】:

我正在准备玩五子棋的应用程序。

我已经创建了漂亮的 GUI,现在我正在使用核心引擎。

有一个类GameEngine 具有同步方法来进行移动。 还有一些实现IClient 接口的类是“玩家”。 首先要做的是实现PlayerClient,它连接到BoardPanel,并在MouseListener inBoardPanel(代表五子棋板)sais 时移动。我希望我的应用程序也可以开放以实现计算机播放器。这就是它如此复杂的原因。

所以...问题: 我的GameEngine 类如下所示:

public class GameEngine {
    private IClient blackPlayer;
    private IClient whitePlayer;
    private GameState gameState;

    ...

    public void play() {
        blackPlayer.run();
        whitePlayer.run();
    }

    public synchronized void move(Move move) throws IllegalMoveException {
        gameState.move(move);
        if (move.isBlackMove()){
            whitePlayer.notify();
        }else{
            blackPlayer.notify();
        }
    }

    public synchronized void waitForYourTurn(boolean color){
        try {
            while (gameState.isBlackToMove()!=color) {
                wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

我的客户端类有 run() 方法:

@Override
    public void run() {
        while (gameState.getWinner() == 0) {
            move = null;
            boolean moved = false;
            while (!moved) {
                gameEngine.waitForYourTurn(black);
                try {
                    //waitForPickedMove();
                    gameEngine.move(move);
                    moved = true;
                } catch (IllegalMoveException e) {
                    e.printStackTrace();
                }
            }
        }
    }

在我写“//waitForPickedMove()”的地方,客户端应该等待用户点击板上的正确位置。所以......我的 Board 类中有一个 MouseListener:

private class MoveMouseListener implements MouseListener {
        @Override
        public synchronized void mouseReleased(MouseEvent e) {
            if (gameState == null) {
                return;
            }

            if (currentMove != null) {
                movePicked();
                repaint();
            }
        }
    }

currentMove 当然是用户通过点击棋盘选择的移动。虽然 movePicked() 看起来像这样:

private synchronized void movePicked() {
    if (gameState.isBlackToMove() && blackClient != null) {
        blackClient.notify();
        clearCurrentMove();
    }
    if (!gameState.isBlackToMove() && whiteClient != null) {
        whiteClient.notify();
        clearCurrentMove();
    }
}

我尝试将wait() 放在很多地方。我试图反转控制并将其放入Board 类中,因此它调用了客户端类中的方法(setCurrentMove(Move move))。不幸的是,我无法达到我想要的。我总是得到 IllegalMonitorStateException 或我的 GUI 块。

我希望我已经充分解释了我的问题。如有任何帮助,我将不胜感激。

编辑:

public class MockComputerClient implements IClient {
    private GameState gameState;
    private GameEngine gameEngine;
    private boolean black;
    private ExecutorService executorService;
    private boolean myTurn = false;
    private int i = 3;

    public MockComputerClient(GameEngine gameEngine, GameState gameState,
            boolean black) {
        this.gameEngine = gameEngine;
        this.gameState = gameState;
        this.black = black;
        executorService = new ExecutorService();
        executorService.run();
    }

    @Override
    public void enemyMoved() {
        myTurn = true;
        executorService.notify();
    }

    private class ExecutorService implements Runnable {

        @Override
        public void run() {
            while (gameState.getWinner() == 0) {
                try {
                    while (!myTurn) {
                        wait();
                    }
                    // Hardcoded Moves
                    gameEngine.move(new Move(black, 3, i++));
                    myTurn = false;
                } catch (InterruptedException | IllegalMoveException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

我很确定我应该同步某事,但我真的不知道是什么。

编辑 2:

@Override
    public void enemyMoved() {
        myTurn = true;
        executorService.move();
    }

    private class ExecutorService implements Runnable {

        @Override
        public void run() {
            while (gameState.getWinner() == 0) {
                System.out.println("Calculating: j= " + j);
                if (j == 3)
                    j = 4;
                else
                    j = 3;

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }

        public void move() {
            while (myTurn) {
                try {
                    gameEngine.move(new Move(black, j, i++));
                    myTurn = false;
                } catch (IllegalMoveException e) {
                    e.printStackTrace();
                }
            }
        }

    }

【问题讨论】:

  • “这就是它如此复杂的原因。” 为了尽快获得更好的帮助,请发布SSCCE 以表明您的最佳尝试,只需单击多线程和鼠标即可。
  • 摇摆。嗯,这是一个很大的应用程序,现在有很多类。我试图删掉重要的东西。 gameEngine 和客户端之间的多线程工作正常。我试图硬编码这个 currentMove。当我尝试等待 GUI 时出现问题。我只是不确定什么应该等待什么以及应该在哪里通知。所以基本上我需要帮助来实现这个评论:移动 waitForPickedMove() 和通知 somhow... 与 movePicked() 中的不完全一样

标签: java multithreading swing exception mouselistener


【解决方案1】:

游戏引擎不需要一直占用一个线程,在无事可做时阻塞它。您应该将其重新设计为反应式:当有事件时,代码在事件处理程序正在执行的线程中执行。那么你就不需要任何wait-notify 机制了。

如果你想让 Player 实现在不占用调用线程的情况下花时间,请在设计 API 时考虑到异步性:IClient 将有一个表示“其他玩家已移动”的方法,它不会返回玩家的移动立即地。 IClient 将通过其移动调用游戏引擎上的方法。

【讨论】:

  • 你能告诉我你的方法的简单例子吗?我想我不明白。多线程是很难的,除非你很好地理解它。 :) 我的意思是例如一个简单的应用程序,其中两个线程相互等待,每个线程都等待鼠标被点击,例如将光标坐标写入控制台。
  • 你的设计先验地将玩家与线程耦合,这是错误的。在 IClient 中放置一个方法,当对方玩家移动时调用该方法;这可以是void 方法(经典回调)。还让游戏引擎拥有一个回调方法,该方法接收来自玩家的移动。人类玩家不需要自己的线程——它响应 GUI 上的操作。计算机播放器也不是线程:它可能在内部使用线程来进行处理。当它的“对面玩家移动”方法被调用时,它会向 ExecutorService 提交一个任务,它会计算...
  • ... 移动并使用其移动调用适当的 Game 方法。
  • 感谢狐狸的解释,现在我将尝试执行您告诉我的内容。我会在这里写下我的结果。
  • 好的,我已经完成了,它适用于 PlayerClient。不幸的是,我在使用 Runnable 类(Executor Service)时仍然遇到问题。我仍然得到这个 IllegalMonitorStateException。我将编辑主要帖子并向您展示我的 MockComputerClient 类。
【解决方案2】:

很难确切地说出它是什么:很多碎片。但看起来您应该按照此处所述同步您的通知调用:

http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#notify()

【讨论】:

    【解决方案3】:

    您好,我向您推荐 sleep() 和 notify()。如果你想在未知时间内停止某个线程,让它休眠,当你再次需要它时,给它 notify()。

    【讨论】:

    • 谢谢,但这里没那么简单。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-09
    • 1970-01-01
    相关资源
    最近更新 更多