为什么我的等待线程即使收到通知也没有醒来?
Why is my waiting thread not waking up even though it's being notified?
我有一个客户端服务器 tic-tac-toe 游戏,它试图 运行 为每个玩家创建一个不同的线程(在不同的终端中),我在 eclipse 中构建了它。
我的目标是让每个玩家移动,.notify() 另一个玩家,然后 .wait() 让另一个玩家移动,并交替执行该过程,直到游戏结束。
toSync 是用于同步的对象
public static final Object toSync = new Object()
并且在播放器 class 中找到(由 XPlayer 和 OPlayer 扩展)。
似乎导致问题的行在 XPlayer 和 Oplayer 中有注释:
Xplayer和OPlayer都有main方法,可以同时运行。 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 迈出了第一步,我就陷入了僵局
我相信只需要那 2 个 classes 就可以解决这个问题,但为了以防万一,这里有 classes Player Board 和 TTTServer:
Class 玩家:
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!!");}
}
}
Class TTT服务器:
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);
}
}
}
Class 董事会:
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("|");
}
}
这是你的错误:
Both Xplayer and OPlayer have main methods, so that they can be run concurrently.
如果您运行宁两个main()
方法,它们不是运行宁"concurrently";它们是完全独立的过程。这意味着没有共享线程、变量、对象、通知等。如果你想共享状态,你需要从一个单一的 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
运行 作为一个单独的客户端同时轮流交替,线程同步是不适合这项工作的工具。您需要在服务器和客户端之间实现自定义消息传递,以保持它们同步。
我有一个客户端服务器 tic-tac-toe 游戏,它试图 运行 为每个玩家创建一个不同的线程(在不同的终端中),我在 eclipse 中构建了它。
我的目标是让每个玩家移动,.notify() 另一个玩家,然后 .wait() 让另一个玩家移动,并交替执行该过程,直到游戏结束。
toSync 是用于同步的对象
public static final Object toSync = new Object()
并且在播放器 class 中找到(由 XPlayer 和 OPlayer 扩展)。
似乎导致问题的行在 XPlayer 和 Oplayer 中有注释:
Xplayer和OPlayer都有main方法,可以同时运行。 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 迈出了第一步,我就陷入了僵局
我相信只需要那 2 个 classes 就可以解决这个问题,但为了以防万一,这里有 classes Player Board 和 TTTServer: Class 玩家:
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!!");}
}
}
Class TTT服务器:
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);
}
}
}
Class 董事会:
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("|");
}
}
这是你的错误:
Both Xplayer and OPlayer have main methods, so that they can be run concurrently.
如果您运行宁两个main()
方法,它们不是运行宁"concurrently";它们是完全独立的过程。这意味着没有共享线程、变量、对象、通知等。如果你想共享状态,你需要从一个单一的 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
运行 作为一个单独的客户端同时轮流交替,线程同步是不适合这项工作的工具。您需要在服务器和客户端之间实现自定义消息传递,以保持它们同步。