使用等待通知方法在 Java 中实现简单的回合制游戏

问题描述 投票:0回答:2

我正在尝试用 Java 实现一个文字游戏,每个玩家轮流从一组随机字母中提取一些字母,然后尝试用这些字母创建一个有效的单词。这就是我到目前为止所拥有的(为了清楚起见,进行了简化):

在 Game 类中,我通过为每个玩家运行一个线程(以及一个用于计时员)来开始游戏。我希望

activePlayers
列表中的第一个玩家(最初与
players
列表相同)迈出第一步,因此我初始化了
turn
turnIndex
属性以对应于该玩家:

public void play()
{
    this.turn = activePlayers.get(0); //the player who joined first goes first
    this.turnIndex = 0; //the player's index in the ArrayList

    for(Player player : players) {
        new Thread(player).start();
    }
    new Thread(new Timekeeper()).start(); //keeps track of the game's duration
}

在 Player 类中,我希望待命的玩家不做任何事情,只是等待当前玩家完成他们的任务,因此是第一个 while 循环。然后,当一个玩家的回合结束时,我希望该线程将监视器交给另一个玩家的线程并等待其下一个回合。这就是我决定采取的方法:

private synchronized boolean submitWord() throws InterruptedException
{
    while(game.turn != this)
    {
        System.out.println(this.name + " is waiting their turn...");
        wait();
    }

    Thread.sleep(1000);

    List<Tile> extracted = game.getBag().extractTiles(wordLength);
    if(extracted.isEmpty())
        return false; //if there are no more letters to extract, the thread ends its execution

    //game logic goes here - creating and validating the word

    //after this player is done, the next player makes their move
    game.turnIndex++;
    if(game.turnIndex >= game.activePlayers.size())
        game.turnIndex = 0;
    game.turn = game.activePlayers.get(game.turnIndex);
    notifyAll();
    return true;
}
@Override
public void run()
{
    do {
        try {
            this.running = this.submitWord();
        } catch(InterruptedException e) {
            System.out.println("Something went wrong with " + this.name + "...");
            e.printStackTrace();
        }
    } while(this.running);

    game.activePlayers.remove(this); //the player is now inactive

    if(game.winner == this)
        System.out.println("Winner: " + this.name + " [" + this.score + " points]");
}

但是,当我尝试运行该程序时,我得到这样的信息:

Player 2 is waiting their turn...
Player 3 is waiting their turn...
1 seconds elapsed...
Player 1: AERIAL [36 points]
Player 1 is waiting their turn...
2 seconds elapsed...
3 seconds elapsed...
4 seconds elapsed...
5 seconds elapsed...
6 seconds elapsed...

基本上,游戏不会超过玩家 1 的第一次尝试,我会陷入无限循环,什么也不会发生。我是否没有正确使用 wait() 和 notifyAll() 方法?我应该如何让玩家线程相互通信?

java multithreading
2个回答
0
投票

如果我正确理解了您的代码,则该

submitWord()
方法属于
Player
类。在多线程开发中,应该使用
synchronized
关键字来获取共享对象的监视器,以限制不同线程同时访问同一资源,避免竞争情况。

就您而言,您正在通过

Player
线程进行同步,这不是正确的设计。您应该通过共享资源(即游戏对象)进行同步。此外,尝试使用同步块而不是整个同步方法,因为后者更有可能阻塞其他线程。

Player
run
方法中,您应该首先检查线程是否可以通过同步块获取游戏的监视器。如果可以,那么您可以通过将游戏对象的
turnIndex
Player
的索引相对来检查是否轮到玩家了。如果不是轮到玩家,那么线程应该调用游戏对象上的
wait()
方法;否则它应该通过调用
submitWord()
来继续。

在您的代码中,您忘记在

notify()
方法中的
notifyAll()
之前添加
return false
(或
submitWord()
)。这种失误可能会导致线程卡住的情况。这是代码的调整版本:

//This method can be called only under the condition the the game's monitor has already been acquired. So, it can only be invoked within a synchronized block.
private boolean submitWord() {
    List<Tile> extracted = game.getBag().extractTiles(wordLength);
    if(extracted.isEmpty()){
        //notify is more efficient than notifyAll, as it causes less overhead by awakening only one random thread instead of all the ones waiting
        this.game.notify();

        //you forgot to call a notify() before returning. 
        //this might have caused your threads to get stuck
        return false;
    }

    //game logic goes here - creating and validating the word
    
    //Rotating the turn
    game.turnIndex = (this.game.turnIndex + 1) % this.game.activePlayers.size();

    game.turn = game.activePlayers.get(game.turnIndex);
    this.game.notify();
    return true;
}

@Override
public void run() {
    do {
        synchronized(this.game){
            if (this.game.indexTurn == this.index){
                this.running = this.submitWord();
                
                //It's better to check here whether the player must be removed or not,
                //as you already own the game's lock
                if (!this.running){
                    this.game.activePlayers.remove(this);
                }
            } else {
                try {
                    this.game.wait();
                } catch(InterruptedException e) {
                    System.out.println("Something went wrong with " + this.name + "...");
                    e.printStackTrace();
                }
            }
        }
    } while(this.running);

    //you should re-acquire the game's lock here since you're modifying the set of players
    //synchronized(this.game){
    //    this.game.activePlayers.remove(this);
    //}

    if(this.game.winner == this){ 
        System.out.println("Winner: " + this.name + " [" + this.score + " points]");
    }
}

0
投票
© www.soinside.com 2019 - 2024. All rights reserved.