我有一个客户端服务器井字游戏,它试图为每个玩家运行一个不同的线程(在不同的终端中),这是我内置的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
在轮流交替时作为单独的客户端运行,则线程同步是该工作的错误工具。您需要在服务器和客户端之间实现自定义消息传递,以使其保持同步。