OK, I just don't understand how threads work. I don't know how I'm supposed to wake up a -specific- thread.
I'm working on the implementation of a simple two-player card game. I have five classes: Card, CardDeck, Dealer, Player and CardGame.
First, I ask for keyboard input for two Strings representing the names of the two players. I make two Players based on those names, and make a new CardDeck.
The CardDeck contains an ArrayList of Cards, which are numbered 0-7. There are 4 cards with each number in the deck. The CardDeck is shuffled after its creation.
Then an instance of Dealer takes the top 8 cards off CardDeck and gives each player four of the cards.
Starting with player1, each player draws a card (now having five in their hand). The player's hand is printed out to the console and the player discards one of their cards, aiming to have four cards all with the same value (4 4 4 4, or 0 0 0 0). If player1 doesn't win at this point, player2 takes their turn and vice versa until one player wins.
CardGame acts as the 'main' class and sets everything in motion. It starts three threads (dealer, player1 and player2) and waits for Dealer to give the signal that the game is over, after which it just cleans up and terminates.
CardGame.java
Code:
vpackage matchGame;
import java.util.Scanner;
/**
* Represents the actual card game. Creates players and deck
* for Dealer to manipulate.
*/
public final class CardGame implements Runnable {
public static void main(String[] args) {
CardGame game = new CardGame();
Thread cardGameThread = new Thread(game);
cardGameThread.start();
}
@Override
public void run() {
Scanner keyboard = new Scanner(System.in);
System.out.println("Game started...");
CardDeck deck = new CardDeck();
Dealer deal = new Dealer(deck);
System.out.print("Please enter the player's name: ");
Player player1 = new Player(keyboard.next(), deck, deal, keyboard);
System.out.print("Please enter the other player's name: ");
Player player2 = new Player(keyboard.next(), deck, deal, keyboard);
deal.setP1(player1);
deal.setP2(player2);
// Player threads currently do nothing.
Thread dealerThread = new Thread(deal);
// Thread player1Thread = new Thread(player1);
// Thread player2Thread = new Thread(player2);
while(!(deal.isFinished)){
// Here I would put a wait() function to wait for the
// dealer thread to finish the game.
}
if(player1.hasWon)
System.out.println(player1.getName() + " is the winner.");
else if(player2.hasWon)
System.out.println(player2.getName() + " is the winner.");
// player1Thread = null;
// player2Thread = null;
keyboard.close();
}
}
Player represents a player and implements Runnable. Each instance has a name and a hand, and a boolean showing whether or not they have won. I also pass in CardDeck and the Scanner object representing the keyboard in order to take keyboard input within the class.
At the moment, the run() method does nothing. This is because I don't know how to implement the multithreading functionality into Player.
private final String name;
public ArrayList<Card> hand;
public final Dealer deal;
public boolean hasWon;
private final CardDeck deck;
private Scanner kb;
public void giveCard(Card newCard){
hand.add(newCard);
}
public Card removeCard(int destroyCard) throws Exception{
Card temp = new Card(destroyCard);
try{
if(!(hand.remove(temp)))
throw new Exception("Specified card is not in your hand!");
}
catch(Exception e){
e.printStackTrace();
}
return temp;
}
public void sortHand(){
Collections.sort(hand);
}
public String toString(){
String returnString = "";
for(Card i : hand)
returnString += " " + i;
return returnString;
}
@Override
public void run() {
// Here I would have a wait() while the dealer deals out cards.
// I would just have another wait() after that to wait for the player's
// turn here. This is probably wrong because I would have no way
// of notifying player1 separately.
}
public synchronized void playTurn(){
System.out.println(deck);
System.out.println("---------------------------------------" +
"----------------------------");
System.out.println(this.getName() + " is Playing ...");
giveCard(deck.draw());
System.out.println(this.getName() + " draws " + hand.get(hand.size() - 1)
+ " from deck.");
this.sortHand();
System.out.println(this.getName() + "'s five cards after DRAWING a" +
" card from deck are: " + this + "(sorted)");
System.out.print(this.getName() + ": Please discard one of the five cards in your hand: ");
try {
int cardToRemove = kb.nextInt();
for(Card i : hand){
if(Integer.valueOf(i.toString()) == cardToRemove){
cardToRemove = hand.indexOf(i);
}
}
deck.discard(hand.remove(cardToRemove));
} catch (Exception e) {e.printStackTrace();}
this.sortHand();
System.out.println(this.getName() + "'s four cards after DISCARD are: " + this);
int a = Integer.valueOf(hand.get(0).toString());
for(Card i : this.hand){
if(a == Integer.valueOf(i.toString()))
this.hasWon = true;
else{
this.hasWon = false;
break;
}
}
notify();
}
}
The code for CardDeck might be necessary for the solution I'm asking for, so here it is. It's a simple ArrayList of instances of Card, and CardDeck contains the methods necessary for drawing cards from the top of the deck and discarding cards to the bottom of the deck.
public CardDeck(){
deck = new ArrayList<Card>();
for(int i = 0; i < 32; i++){
deck.add(new Card(i % 8));
}
Collections.shuffle(deck);
}
public Card draw(){
return deck.remove(0);
}
public void discard(Card thisCard){
deck.add(thisCard);
}
@Override
public String toString(){
String returnString = "";
for(Card i : deck)
returnString += i + " ";
return returnString;
}
}
Added the code for Dealer below. Dealer just represents the dealer and has one job: to deal four cards to each player at the beginning of the game. This code is inside its own class because I'm following the instructor (otherwise I'd just have deal() in CardGame).
Dealer pretty much runs the entire game once its thread is started. This is because I haven't figured out the multithreading part of the exercise yet. Preferably, at the very end of Dealer's run() method it would notify CardGame to let it know that the game has finished.
Dealer.java
Code:
package matchGame;
public class Dealer implements Runnable{
private CardDeck deck;
public boolean isFinished;
private Player player1;
private Player player2;
public Dealer(CardDeck deck){
this.deck = deck;
isFinished = false;
}
public void setP1(Player player1){
this.player1 = player1;
}
public void setP2(Player player2){
this.player2 = player2;
}
public synchronized void deal(Player toPlayer){
for(int i = 0; i < 4; i++){
toPlayer.hand.add(deck.draw());
}
notify();
}
@Override
public void run() {
deal(player1);
deal(player2);
System.out.println("Dealer deals four cards to the players as follows...\n"
+ player1.getName() + "'s FIRST four cards are: " + player1 + "\n"
+ player2.getName() + "'s FIRST four cards are: " + player2 + "\n");
while((!(player1.hasWon)) && (!(player2.hasWon))){
// Here I would add a wait() while player1 plays their turn.
player1.playTurn();
if(player1.hasWon)
break;
// Here I would add another wait() while player2 plays their turn.
player2.playTurn();
}
isFinished = true;
notify();
}
}
What's the correct implementation for this? After starting the two Player threads, Player1 should go first immediately. When Player1's turn is done (and Player1 hasn't won yet), Player2 should be notified and Player1 should go to sleep and vice versa. And of course, when either Player1 or Player2 hasWon, the main() thread should keep going.
Any help would be greatly appreciated. I'm just having trouble grasping how this is supposed to work.
2014-02-20, 07:05 PM
Locked
Re: Java threads and synchronization
Why do you need threads?
Adding concurrency will only complicate your program considering how simple it is.
2014-02-20, 07:09 PM
Jedward
Re: Java threads and synchronization
Quote:
Originally Posted by Locked
Why do you need threads?
Adding concurrency will only complicate your program considering how simple it is.
Assignment requirement.
2014-02-20, 07:17 PM
Locked
Re: Java threads and synchronization
Not sure how to help you. You need to give more information. If this is your real code, I suggest fixing some of the errors in it too.
Code:
while((!player1.hasWon) && (!player2.hasWon));
is pretty much a logic error.
2014-02-20, 07:45 PM
Jedward
Re: Java threads and synchronization
Added full implementations of four of the classes to the OP, mess and all. I don't know what other information I can give; I'm cleaning up the explanation of how the program's supposed to work right now and will edit it into OP as soon as it's done. I put an overview of the game into the first part of the OP.
@Locked did you spot any other errors? I don't actually think that loop is an error, but maybe you can tell me why I'm wrong. That loop just does nothing while checking if player1 or player2 have won, so as soon as either of the booleans return true (false with the negation), the loop ends and the last part of CardGame runs.
2014-02-20, 07:59 PM
Locked
Re: Java threads and synchronization
Quote:
Originally Posted by Jedward
Locked did you spot any other errors? I don't actually think that loop is an error, but maybe you can tell me why I'm wrong. That loop just does nothing while checking if player1 or player2 have won, so as soon as either of the booleans return true (false with the negation), the loop ends and the last part of CardGame runs.
The loop does nothing. It terminates because it reaches the semicolon.
You say you want to wake a thread but none of the threads are asleep.\
You say you want to wake a thread but none of the threads are asleep.
player1Thread and player2Thread should be asleep when they start. At least that's what I can see from running it (nothing happens because they wait for an InterruptedException).
When the threads start, run() is called, which calls go(). The first bit of go calls wait(), which does nothing until an InterruptedException is thrown, waking the player up and starting their turn.
At the moment, when I run the code, nothing happens after the threads are started. Both are waiting to be woken up. What wakes them up, I don't know.
I can't call notify or notifyAll from main, which means:
Dealer should be a thread and somehow notify player1 when it's done dealing?
notify should happen somewhere in the Player thread?
go() shouldn't wait and instead, the draw() and discard() methods in CardDeck should be synchronized instead?
2014-02-20, 08:09 PM
Locked
Re: Java threads and synchronization
Quote:
Originally Posted by Jedward
So the loop solution is...
Code:
while((!player1.hasWon) && (!player2.hasWon)){
}
?
Yes
Quote:
At the moment, when I run the code, nothing happens after the threads are started. Both are waiting to be woken up. What wakes them up, I don't know.
Try notify or notifyAll.
I never realised that `wait` is a form of sleeping in Javaland.
2014-02-25, 05:07 PM
happylight
Re: Java threads and synchronization
@Jedward; Did you figure this out? You just need to figure out when threads need to wait and when they need to notify another thread.
Dealer/CardGame/Main thread: Notify players when it's their turn. Wait during their turn.
Player: Wait when it's not my turn. Notify dealer when my turn is over.
Also there's an extra semicolon in Player.removeCard()
Code:
if(!(hand.remove(temp)));
2014-02-27, 06:06 PM
Jedward
Re: Java threads and synchronization
Quote:
Originally Posted by happylight
Jedward Did you figure this out? You just need to figure out when threads need to wait and when they need to notify another thread.
Dealer/CardGame/Main thread: Notify players when it's their turn. Wait during their turn.
Player: Wait when it's not my turn. Notify dealer when my turn is over.
I've been stewing over this while working on other things these past few days.
Game's working but not threaded properly. I keep getting IllegalMonitorStateExceptions when I try to implement the wait/notify system, assuming it's because I haven't figured out exactly where wait() and notify() should go in.
I guess I still haven't figured out the question I posed at the very beginning of this thread: Can I wake up a specific thread instead of being at the mercy of luck? If so, how?
The run() functions here are copypasted from the first post, with comments on where I would put wait() functions to help explain my thought process.
CardGame.java
Code:
public void run() {
Scanner keyboard = new Scanner(System.in);
System.out.println("Game started...");
CardDeck deck = new CardDeck();
Dealer deal = new Dealer(deck);
System.out.print("Please enter the player's name: ");
Player player1 = new Player(keyboard.next(), deck, deal, keyboard);
System.out.print("Please enter the other player's name: ");
Player player2 = new Player(keyboard.next(), deck, deal, keyboard);
deal.setP1(player1);
deal.setP2(player2);
// Player threads currently do nothing.
Thread dealerThread = new Thread(deal);
// Thread player1Thread = new Thread(player1);
// Thread player2Thread = new Thread(player2);
while(!(deal.isFinished)){
// Here I would put a wait() function to wait for the
// dealer thread to finish the game.
}
if(player1.hasWon)
System.out.println(player1.getName() + " is the winner.");
else if(player2.hasWon)
System.out.println(player2.getName() + " is the winner.");
// player1Thread = null;
// player2Thread = null;
keyboard.close();
}
Dealer.java
Code:
public void run() {
deal(player1);
deal(player2);
System.out.println("Dealer deals four cards to the players as follows...\n"
+ player1.getName() + "'s FIRST four cards are: " + player1 + "\n"
+ player2.getName() + "'s FIRST four cards are: " + player2 + "\n");
while((!(player1.hasWon)) && (!(player2.hasWon))){
// Here I would add a wait() while player1 plays their turn.
player1.playTurn();
if(player1.hasWon)
break;
// Here I would add another wait() while player2 plays their turn.
player2.playTurn();
}
isFinished = true;
notify();
}
Player.java
Code:
public void run() {
// Here I would have a wait() while the dealer deals out cards.
// I would just have another wait() after that to wait for the player's
// turn here. This is probably wrong because I would have no way
// of notifying player1 separately.
}
Cleaned up first post and this post to reflect the new code. Currently the game works, but isn't actually using multithreading.
2014-02-28, 08:02 PM
happylight
Re: Java threads and synchronization
You can wake up specific thread by using notify().
E.g. When the dealer wants to wake up a waiting player 1, use player1.notify();
When player 1 wants to wake up the dealer, use dealer.notify(); Players need to have a reference to the dealer of course.