2016-03-07 65 views
1

我有一個客戶端服務器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("|"); 
    } 
} 
+3

爲任何人想要閱讀和解決的方法太多代碼(免費:)) – pczeus

回答

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運行作爲一個獨立的客戶端,同時交替輪流,線程同步是在錯誤的工具工作。您需要在您的服務器和客戶端之間實現自定義消息傳遞,以使其保持同步。

+0

非常感謝您的澄清,這是一個微妙的關於Java,我不知道。我所面臨的唯一嚴格的限制是兩名球員必須在兩個不同的終端或控制檯中。正如我所理解的那樣,爲每一個實現主要功能是實現這一目標的唯一方法。有沒有一種方法來實現你已經顯示的方式,通過從一個普通的主要方法啓動兩個線程,同時讓x和o顯示在不同的控制檯(在eclipse中)? –

+0

您可能想補充說,它們運行在兩個完全不知道彼此的獨立JVM中。 –

+0

@SebastianC如果他們需要在單獨的控制檯中,那意味着他們應該作爲獨立客戶運行。所以你將無法使用線程同步。 – shmosel

相關問題