为什么我的等待线程没有被唤醒,即使它被通知了?

时间:2016-03-07 02:49:44

标签: java multithreading deadlock wait notify

我有一个客户端服务器tic-tac-toe游戏,它试图为每个玩家运行一个不同的线程(在不同的终端),这是我在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循环的大括号(我知道到目前为止我已经完成的调试是真的。)

Class 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);
        }
    }

Class 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("|");
    }
}

1 个答案:

答案 0 :(得分:3)

这是你的错误:

  

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在轮流交替时作为单独的客户端运行,那么线程同步就是错误的工具。您需要在服务器和客户端之间实现自定义消息传递,以使它们保持同步。