0

I am trying to implement threads where one thread generates a random number while another thread waits once it generates random number it should notify and wait for the other thread to do the same. I am getting illegal monitor state exception, please help me out and point out my mistake

class Dice
{
    int diceValue;

    public Dice()
    {
        this.diceValue=0;
    }
}
public class DiceGame  implements Runnable
{
    Dice d;

public DiceGame()
{
    this.d=new Dice();
}
public void run()
{
    if(Thread.currentThread().getName().equals("Player 1"))
    {
        Random rg=new Random();
        for(int i=0;i<6;i++)
        {
            synchronized(d)
            {

                d.diceValue=rg.nextInt(6);
                System.out.println(Thread.currentThread().getName()+" dice Value is "+d.diceValue);
                d.notifyAll();
                try 
                {
                    d.wait();
                }

                catch (InterruptedException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    else if(Thread.currentThread().getName().equals("Player 2"))
    {
        Random rg=new Random();
        for(int i=0;i<6;i++)
        {
            synchronized(d)
            {
                try 
                {
                    d.wait();
                } 
                catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
                d.diceValue=rg.nextInt(6);
                System.out.println(Thread.currentThread().getName()+"dice Value is ");
                d.notifyAll();
            }
        }
    }
}



public static void main(String []args)
{
    DiceGame dg=new DiceGame();

    Thread tr1=new Thread(dg);
    Thread tr2=new Thread(dg);

    tr1.setName("Player 1");
    tr2.setName("Player 2");

    tr1.start();
    tr2.start();
}
}
Kunal Surana
  • 659
  • 5
  • 14
Akshay Bhat
  • 236
  • 3
  • 16
  • 1
    this is a horrible example of using wait/notifyAll, but it doesn't generate an IllegalMonitorStateException. – Nathan Hughes Feb 24 '16 at 19:07
  • 1
    Why @Akshay Bhat, why? Why did you write such a contrived example code? Aren't there enough resources that help you gently start getting introduced to the thorny topic of concurrency and multithreading? Or were you more interested in rather bravely doing it for the sake of it? It is rather difficult for me to comprehend that you'd want to write this kind of (difficult) code if you starting to do it. The [Java concurrency](https://docs.oracle.com/javase/tutorial/essential/concurrency/) tutorial is well-written. What not through it first? – Kedar Mhaswade Feb 24 '16 at 21:13

3 Answers3

4
        synchronized(d)
        {
            try 
            {
                d.wait();

Any time you see an unconditional call to wait, you know there's a bug right there. Before you wait, you have to make sure the thing you're waiting for didn't already happen. That's the reason you entered synchronized a block, right?

The whole point of the wait/notify mechanism is that you can atomically release a lock and await notification. That can't possibly work if you don't check the predicate (the thing that you're waiting for) before calling wait.

Here synchronized block is necessary to hold the monitor when calling wait.

Right, because unless you're inside a synchronized block, there's no way you can tell whether the thing you're waiting for has already happened or not. And since you must check whether it's already happened before you wait for it, you can only call wait from inside a synchronized block. But you didn't check! You understand the requirement but not its rationale, so you formally met it, but still managed to create the very problem the requirement is designed to prevent!

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • I would disagree. You can wait for a thing which has not happened yet, that is the point for waiting, no? You can use the synchronized block without waiting for anything too. Here synchronized block is necessary to hold the monitor when calling wait. See also http://www.programcreek.com/2011/12/monitors-java-synchronization-mechanism/ – Gee Bee Feb 24 '16 at 19:11
  • @GeeBee It's not that you *can* wait for a thing which has not happened yet, it's that you **must** wait for a thing which has not happened yet. Since the thing can occur when you don't hold the lock, after you get the lock, you must check that the thing didn't happen before you acquired the lock. That's *why* the synchronized block is necessary to call `wait`. You held it, but totally missed the reason for that requirement and so nothing keeps your code from waiting for something that has already happened. You missed the whole logic of an atomic "unlock and wait" operation! – David Schwartz Feb 24 '16 at 19:25
0

I guess, the problem is that you are notifying every other thread before waiting youself.

 d.notifyAll();
 try 
 {
    d.wait();
 }

Refer to this post: https://stackoverflow.com/a/828341/5602214

Community
  • 1
  • 1
Prashant
  • 4,775
  • 3
  • 28
  • 47
-1

Your code could be improved in several ways, but with a few little hacks it can work:

class Dice
{
    int diceValue;

    public Dice()
    {
        this.diceValue=0;
    }
}
public class DiceGame  implements Runnable
{
    Dice d;

    public DiceGame()
    {
        this.d=new Dice();
    }
    @Override
    public void run()
    {
        if(Thread.currentThread().getName().equals("Player 1"))
        {
            final Random rg=new Random();
            for(int i=0;i<6;i++)
            {
                synchronized(d)
                {

                    d.diceValue=rg.nextInt(6);
                    System.out.println(Thread.currentThread().getName()+" dice Value is "+d.diceValue);
                    d.notifyAll();

                    try
                    {
                        d.wait();
                    }

                    catch (final InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        else if(Thread.currentThread().getName().equals("Player 2"))
        {
            final Random rg=new Random();
            for(int i=0;i<6;i++)
            {
                synchronized(d)
                {
                    try
                    {
                        d.wait();
                    }
                    catch (final InterruptedException e)
                    {
                        e.printStackTrace();
                    }

                    d.diceValue=rg.nextInt(6);

                    System.out.println(Thread.currentThread().getName()+" dice Value is "+d.diceValue);
                    d.notifyAll();
                }
            }
        }
    }



    public static void main(final String []args) throws InterruptedException
    {
        final DiceGame dg=new DiceGame();

        final Thread tr1=new Thread(dg);
        final Thread tr2=new Thread(dg);

        tr1.setName("Player 1");
        tr2.setName("Player 2");

        tr2.start();
        Thread.sleep(100);
        tr1.start();
    }
}

I got no illegalMonitorstate exception, but the first thread get locked up forever. Basically the problem is that the first thread rolls the dice, and calls d.notifyAll before actually the 2nd thread could start and waiting for the dice. This is naively solved by first starting thread 2 then waiting a bit and start thread 1.

You might also consider:

  • using the Java convention for braces { }
  • rg.nextInt gives values between 0..5, not 1..6
  • it is bad idea to make the thread code work differently depending on the name of the thread. In OOP different behavior is expressed with descendant classes instead.

I guess you're wishing for an universal solution for rolling the dice with multiple players. This problem is not necessarily a problem which requires concurrent programming, since rolling the dice goes serially amongst players. You can of course have the players as Threads, but only one Thread will be active at any point of time. In case of using Threads, you shall implement your own scheduling logic which ensures consistent one-after-another scheduling of threads. Using a monitor (e.g. synchornize(d)) does not offer any guarantee of the ordering, it is only built to guarantee that up to one thread can access to the dice at any point in time.

A solution with any number of players but no threads (this is not a concurrency problem after all) shows this behavior:

import java.util.Random;

class Dice {
    private final Random rg=new Random();
    private int diceValue=1;

    public void roll() {
        diceValue=rg.nextInt(6)+1;
    }

    @Override
    public String toString() {
        return "value="+diceValue;
    }
}

public class Player extends Thread {
    Dice dice;
    int rollsLeft=6;

    public Player(final Dice dice) {
        this.dice=dice;
    }

    @Override
    public void run() {
        while (rollsLeft>0) {
            synchronized(dice) {
                // dice obtained
                dice.roll();
                System.out.println(Thread.currentThread().getName()+" rolled "+dice);
            }
            // dice released
            rollsLeft--;

            // just wait a little to make it slower and let other threads to join 
            try {
                Thread.sleep(100);
            } catch (final InterruptedException e) {
                // ignore
            }
        }
    }

    public static void main(final String []args) throws InterruptedException {
        final Dice dice=new Dice();
        final Player player1=new Player(dice);
        final Player player2=new Player(dice);

        player1.start();
        player2.start();
    }
}

Which gives:

Thread-0 rolled value=1
Thread-1 rolled value=6
Thread-0 rolled value=2
Thread-0 rolled value=4
Thread-1 rolled value=2
etc...

As you can see, the order (i.e. player1, player2, player1, player2) is not guaranteed.

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

class Dice {
    private final Random rg=new Random();
    private int diceValue=1;

    public void roll() {
        diceValue=rg.nextInt(6)+1;
    }

    @Override
    public String toString() {
        return "value="+diceValue;
    }
}

public class Player {
    Dice dice;
    String player;

    public Player(final Dice dice,final String player) {
        this.dice=dice;
        this.player=player;
    }

    public void roll() {
        dice.roll();
        System.out.println(player+" rolled "+dice);
    }

    public static void main(final String []args) throws InterruptedException {
        final Dice dice=new Dice();
        final List<Player> players=new ArrayList<Player>();
        players.add(new Player(dice,"Ann"));
        players.add(new Player(dice,"Ben"));
        players.add(new Player(dice,"Cecil"));
        players.add(new Player(dice,"Denise"));

        for (int rounds=0;rounds<6;rounds++) {
            System.out.println("---");
            for (final Player player:players) {
                player.roll();
            }
        }
    }
}

Which gives you the expected output, i.e. Ann, Ben, Cecil, Denise has 6 rounds of rolling the dice.

Gee Bee
  • 1,794
  • 15
  • 17
  • This still calls `wait` without checking the predicate. That will never work. – David Schwartz Feb 24 '16 at 18:53
  • Please consider running the code. It actually works. The "trick" in the original code was that thread1 starts with notify and thread2 starts with wait. – Gee Bee Feb 24 '16 at 19:03
  • He might not see your comment if you don't mention him. @DavidSchwartz – DavidS Feb 24 '16 at 19:04
  • @GeeBee If it works, it works by luck. Nothing prevents the thread from waiting for an event that has already happened. – David Schwartz Feb 24 '16 at 19:08
  • @DavidSchwartz that was a kind of the problem of the original approach. As I commented, the "hack" is to create thread2 first, start it, wait some reasonable time. This ensures that thread2 stats in wait. Then start thread1 which rolls the dice, and then calls notify. The "solution" is working, i.e. it makes the code work reliably. Of course it is a piece of hack as I explained in details. **Please, run the actual code before downvote.** I found impolite to downvote an answer from the sleeve without a valid reason. – Gee Bee Feb 24 '16 at 19:17
  • @GeeBee It can't work reliably. No amount of waiting, no matter how "reasonable" you think it is, provides reliable thread synchronization. Also, I didn't downvote. – David Schwartz Feb 24 '16 at 19:28
  • Schwartz's reason seems completely valid to me. Code that relies on "waiting some reasonable time" is error-prone. You've specified a 100ms wait. This could be easily spoiled by any lag in the system. On top of that, [`Thread.sleep` is unreliable](https://stackoverflow.com/questions/18736681/how-accurate-is-thread-sleep). Instead of solving the actual problem, this code just puts a bandaid on it. – DavidS Feb 24 '16 at 19:28
  • And you don't have to fix everything. It's sufficient to answer the OP's question. But you *must* point out obvious brokenness so that you don't mislead the OP into thinking other parts of his code are not broken. You don't have to fix everything, but you can't suggest that bad practices are anything but bad practices. – David Schwartz Feb 24 '16 at 19:30
  • Good point, @DavidSchwartz, I shall avoid posting full code answers, and I shall avoid posting too long to read answers at late night :) As I highlighted, the whole idea of using wait and notify to solve this problem is *error prone by design.* Sorry that my answer was not clear enough. – Gee Bee Feb 25 '16 at 11:59