我有一个客户端服务器井字游戏,它试图为每个玩家运行一个不同的线程(在不同的终端中),这是我内置的Eclipse。

我的目标是让每个玩家做出自己的动作,.notify()成为另一位玩家,然后.wait()让另一位玩家做出自己的动作,并交替该过程直到游戏完成。

toSync是用于同步的对象

public static final Object toSync = new Object()


并且可以在Player类中找到(它由XPlayer和OPlayer扩展)。

在XPlayer和Oplayer中注释了可能引起问题的行:

Xplayer和OPlayer都有主要方法,因此它们可以同时运行。 X进行第一个举动,然后使用套接字将此举动传达给服务器。
服务器将这一举动传递给O,然后O进行自己的举动并将其传递回服务器。直到游戏结束为止。

在x播放器上执行第一个动作时效果很好,但是一旦进行了初始动作,则o应该显示棋盘,然后提示用户进行该动作。但是,这不会发生:x进行移动,并且o被通知已通知,但实际上从未唤醒。永远不会到达在OPlayer中注释了while循环结尾的花括号(到目前为止,通过调试,我知道这是正确的)。

XPlayer类:

import java.io.*;

public class XPlayer
extends Player
implements Runnable
{
    public static volatile boolean xTurn = true;

    public XPlayer() throws IOException
    {
        super();
        mark = LETTER_X;
    }

    public void run()
    {
        try
        {
            System.out.println("Okay " + name + ", You will be the X-Player");

            synchronized(toSync)
            {
                Cell move = makeMove();
                out.println(move.toString());
                board.addMark
                    (move.row(),move.col(),move.mark());
                board.display();

                xTurn = false;
                toSync.notifyAll();    //THIS IS THE LINE THAT ISNT WORKING!!

                System.out.println(WAITING);
            }
            synchronized(toSync)
            {
                while (!xTurn)
                    {toSync.wait();}
            }

            while (!board.isOver())
            {
                synchronized(toSync)
                {
                    String line;
                    do {line = in.readLine();}
                    while (line == null);

                    Cell opponentMove = Cell.split(line);
                    board.addMark
                        (opponentMove.row(),opponentMove.col(), opponentMove.mark());

                    String move = makeMove().toString();
                    out.println(move);
                    xTurn = false;
                    toSync.notifyAll();

                    while (!xTurn)
                        {toSync.wait();}
                }
            }

            endGame();

            sock.close();
            in.close();
            stdin.close();
            out.close();

        } catch (InterruptedException ie)
        {
            System.out.println("IE IN XPLAYER! " + ie.getMessage());
            System.exit(1);

        } catch (IOException ioe)
        {
            System.out.println("IOE IN XPLAYER! " + ioe.getMessage());
            System.exit(1);
        }
    }
    public static void main(String[] args)
    {
        try
        {
            XPlayer x = new XPlayer();
            Thread t = new Thread(x);
            t.start();

        } catch(IOException ioe)
        {
            System.err.println
                ("IOE IN XPLAYER MAIN " + ioe.getMessage());
            System.exit(1);
        }
    }


OPlayer类:

import java.io.*;

public class OPlayer
extends Player
implements Runnable
{

    public OPlayer() throws IOException
    {
        super();
        mark = LETTER_O;
    }

    public void run()
    {
        try
        {
            synchronized(toSync)
            {
                System.out.println("Okay " + name + ", You will be the O-Player");

                System.out.println(WAITING);
                while(!XPlayer.xTurn)
                    {toSync.wait();}    // THIS IS THE LINE THAT ISN'T WAKING UP

                while (!board.isOver())
                {
                    String line;

                    do {line = in.readLine();}
                    while (line == null);

                    Cell opponentMove = Cell.split(line);
                    board.addMark
                        (opponentMove.row(),opponentMove.col(),opponentMove.mark());

                    Cell move = makeMove();
                    out.println(move.toString());
                    board.addMark(move.row(),move.col(),move.mark());
                    board.display();
                    XPlayer.xTurn = true;
                    toSync.notifyAll();

                    System.out.println(WAITING);
                    while (XPlayer.xTurn)
                        {toSync.wait();}
                }
            }

            endGame();

            sock.close();
            in.close();
            stdin.close();
            out.close();

        } catch (InterruptedException ie)
        {
            System.out.println("IE IN OPLAYER " + ie.getMessage());
            System.exit(1);

        } catch (IOException ioe)
        {
            System.err.println("IOE IN OPLAYER " + ioe.getMessage());
            System.exit(1);
        }
    }

    public static void main(String[] args)
    {
        try
        {
            OPlayer o = new OPlayer();
            Thread t = new Thread(o);
            t.start();

        } catch(IOException ioe)
        {
            System.err.println("IOE IN OPLAYER MAIN" + ioe.getMessage());
            System.exit(1);
        }
    }
}


如代码所示,XPlayer中的toSync.notifyAll()调用不会唤醒OPlayer线程,一旦XPlayer做出了第一步,我就陷入了僵局

我相信只需要解决这两个类就可以解决问题,但以防万一,以下是Player Board和TTTServer类:
班级球员:

import java.net.*;
import java.io.*;

public class Player
implements Constants
{
    protected static final Object toSync = new Object();

    protected Socket sock;
    protected BufferedReader stdin;
    protected BufferedReader in;
    protected PrintWriter out;

    protected String name;
    protected char mark;
    protected Board board;

    public Player() throws IOException
    {
        sock = new Socket("localhost",1298);
        stdin = new BufferedReader(new InputStreamReader(System.in));
        in = new BufferedReader(new
                InputStreamReader(sock.getInputStream()));
        out = new PrintWriter(sock.getOutputStream(),true);

        System.out.println(WELCOME);
        System.out.println("Please Enter your name:");
        name = stdin.readLine();
        board = new Board();
    }

    public Cell makeMove() throws IOException
    {
        board.display();

        int row = -1;
        int col = -1;
        do
        {
            while (row < 0 || row > 2)
            {
                System.out.println
                    (name + ", what row would you like your next move to be in?");
                row = Integer.parseInt(stdin.readLine());
                if (row < 0 || row > 2)
                    {System.out.println("Invalid entry! Try again...");}
            }
            while (col < 0 || col > 2)
            {
                System.out.println
                    (name + ", what column would you like your next move to be in?");
                col = Integer.parseInt(stdin.readLine());
                if (col < 0 || col > 2)
                    {System.out.println("Invalid entry! Try again...");}
            }

            if (board.getMark(row, col) != SPACE_CHAR)
                {System.out.println("That spot is already taken Try again...");}

        } while (board.getMark(row,col) != SPACE_CHAR);

        return new Cell(row,col,mark);
    }

    public void endGame()
    {
        if (board.xWins() == 1)     {System.out.println(END + XWIN);}
        if (board.oWins() == 1)     {System.out.println(END + OWIN);}
        else            {System.out.println(END + " It was a tie!!");}
    }
}


TTTServer类:

import java.net.*;
import java.io.*;

public class TTTServer
implements Constants
{
    public static void main(String[] args)
    {
        try
        {
            ServerSocket ss = new ServerSocket(1298,2);
            System.out.println("The Server is running...");
            Socket sock;

            Board board = new Board();

            sock = ss.accept();
            sock = ss.accept();

            BufferedReader in = new BufferedReader(new
                    InputStreamReader(sock.getInputStream()));
            PrintWriter out = new PrintWriter(sock.getOutputStream(),true);

            do
            {
                String moveString;

                do {moveString = in.readLine();}
                while (moveString == null);

                Cell move = Cell.split(moveString);
                board.addMark(move.row(), move.col(), move.mark());

                out.println(moveString);

            } while(!board.isOver());

            in.close();
            out.close();
            ss.close();
            sock.close();

        } catch(IOException ioe)
        {
            System.out.println("IOE IN TTTSERVER " + ioe.getMessage());
            System.exit(1);
        }

    }
}


类板:

public class Board
implements Constants
{
    /**
     * A 2D char array stores the game board and
     * the total number of marks
     */
    private char theBoard[][];
    private int markCount;
    /**
     * Default constructor initializes the array and fills it with
     * SPACE_CHARs from the Constants interface
     */
    public Board()
    {
        markCount = 0;
        theBoard = new char[3][];
        for (int i = 0; i < 3; i++) {
            theBoard[i] = new char[3];
            for (int j = 0; j < 3; j++)
                theBoard[i][j] = SPACE_CHAR;
        }
    }
    /**
     * Getter for the mark at the location specified by the arguments
     *
     * @param row
     * @param column
     *
     * @return mark
     */
    public char getMark(int row, int col)
        {return theBoard[row][col];}
    /**
     * Getter for the number of moves which have been made thus far
     *
     * @return markCount
     */
    public int getMarkCount()       {return markCount;}
    /**
     * @return true if the game is over, otherwise false
     */
    public boolean isOver()
    {
        if (xWins() == 1 || oWins() == 1 || isFull())
            {return true;}

        return false;
    }
    /**
     * @return true if the board has been completely filled with
     * X_CHARs and O_CHARs from Constants interface, else false
     */
    public boolean isFull()
        {return markCount == 9;}
    /**
     * Runs checkWinner on LETTER_X from Constants interface
     *
     * @return true if X has won, else false
     */
    public int xWins()
        {return checkWinner(LETTER_X);}
    /**
     * Runs checkWinner on LETTER_O from Constants interface
     *
     * @return true if O has won, else false
     */
    public int oWins()
        {return checkWinner(LETTER_O);}
    /**
     * Uses the formatting helper methods to display the board
     * in the console
     */
    public void display()
    {
        displayColumnHeaders();
        addHyphens();
        for (int row = 0; row < 3; row++) {
            addSpaces();
            System.out.print("    row " + row + ' ');
            for (int col = 0; col < 3; col++)
                System.out.print("|  " + getMark(row, col) + "  ");
            System.out.println("|");
            addSpaces();
            addHyphens();
        }
    }
    /**
     * Add the mark in the last argument to the location specified by the
     * first two arguments
     *
     * @param row
     * @param column
     * @param mark
     */
    public void addMark(int row, int col, char mark)
    {
        theBoard[row][col] = mark;
        markCount++;
    }
    /**
     * Clears the board by replacing all marks with
     * SPACE_CHARs from the Constants interface
     */
    public void clear()
    {
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                theBoard[i][j] = SPACE_CHAR;
        markCount = 0;
    }
    /**
     * Checks if the player with the argument mark has won the game
     *
     * @param mark
     *
     * @return true if the game was won, else false
     */
    int checkWinner(char mark) {
        int row, col;
        int result = 0;

        for (row = 0; result == 0 && row < 3; row++) {
            int row_result = 1;
            for (col = 0; row_result == 1 && col < 3; col++)
                if (theBoard[row][col] != mark)
                    row_result = 0;
            if (row_result != 0)
                result = 1;
        }


        for (col = 0; result == 0 && col < 3; col++) {
            int col_result = 1;
            for (row = 0; col_result != 0 && row < 3; row++)
                if (theBoard[row][col] != mark)
                    col_result = 0;
            if (col_result != 0)
                result = 1;
        }

        if (result == 0) {
            int diag1Result = 1;
            for (row = 0; diag1Result != 0 && row < 3; row++)
                if (theBoard[row][row] != mark)
                    diag1Result = 0;
            if (diag1Result != 0)
                result = 1;
        }
        if (result == 0) {
            int diag2Result = 1;
            for (row = 0; diag2Result != 0 && row < 3; row++)
                if (theBoard[row][3 - 1 - row] != mark)
                    diag2Result = 0;
            if (diag2Result != 0)
                result = 1;
        }
        return result;
    }

    /**
     * The final three helper methods are called by display
     * to format the board properly in the console
     */
    void displayColumnHeaders() {
        System.out.print("          ");
        for (int j = 0; j < 3; j++)
            System.out.print("|col " + j);
        System.out.println();
    }

    void addHyphens() {
        System.out.print("          ");
        for (int j = 0; j < 3; j++)
            System.out.print("+-----");
        System.out.println("+");
    }

    void addSpaces() {
        System.out.print("          ");
        for (int j = 0; j < 3; j++)
            System.out.print("|     ");
        System.out.println("|");
    }
}

最佳答案

这是您的错误:


  Xplayer和OPlayer都有主要方法,因此它们可以同时运行。


如果您正在运行两个main()方法,则它们不是“并发”运行的;它们是完全独立的过程。这意味着没有共享线程,变量,对象,通知等。如果要共享状态,则需要从单个main()方法启动所有操作:

class StarterClass {
    public static void main(String[] args)
    {
        // start XPlayer thread
        try
        {
            XPlayer x = new XPlayer();
            Thread t = new Thread(x);
            t.start();

        } catch(IOException ioe)
        {
            System.err.println
                ("IOE IN XPLAYER MAIN " + ioe.getMessage());
            System.exit(1);
        }

        // start OPlayer thread
        try
        {
            OPlayer o = new OPlayer();
            Thread t = new Thread(o);
            t.start();

        } catch(IOException ioe)
        {
            System.err.println("IOE IN OPLAYER MAIN" + ioe.getMessage());
            System.exit(1);
        }
    }
}


如果您打算让每个Player在轮流交替时作为单独的客户端运行,则线程同步是该工作的错误工具。您需要在服务器和客户端之间实现自定义消息传递,以使其保持同步。

07-24 09:49