为什么我的等待线程即使被通知也不醒来?
我有一个客户端服务器tic-tac-toe游戏,它试图为每个玩家运行不同的线程(在不同的终端),这是我在eclipse中构建的。为什么我的等待线程即使被通知也不醒来?
我的目标是让每个玩家进行移动,.notify()另一个玩家,然后.wait()让另一个玩家进行移动,并且交替执行该过程直到完成游戏。
toSync是用于同步
public static final Object toSync = new Object()
物体,它是在播放器类(其由两个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发起了第一步,就会陷入僵局。我相信只有那两个类才能解决问题,但为了以防万一,以下是这些类的玩家公猪队d和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
运行作为一个独立的客户端,同时交替轮流,线程同步是在错误的工具工作。您需要在您的服务器和客户端之间实现自定义消息传递,以使其保持同步。
非常感谢您的澄清,这是一个微妙的关于Java,我不知道。我所面临的唯一严格的限制是两名球员必须在两个不同的终端或控制台中。正如我所理解的那样,为每一个实现主要功能是实现这一目标的唯一方法。有没有一种方法来实现你已经显示的方式,通过从一个普通的主要方法启动两个线程,同时让x和o显示在不同的控制台(在eclipse中)? –
您可能想补充说,它们运行在两个完全不知道彼此的独立JVM中。 –
@SebastianC如果他们需要在单独的控制台中,那意味着他们应该作为独立客户运行。所以你将无法使用线程同步。 – shmosel
为任何人想要阅读和解决的方法太多代码(免费:)) – pczeus