1

I was trying to read up on Producer Consumer solution and I came across this code:

package SampleProjects;

public class ProducerConsumerTest {

    public static void main(String[] args) {
        CubbyHole c = new CubbyHole();
        Producer p1 = new Producer(c, 1);
        Consumer c1 = new Consumer(c, 1);
        p1.start();
        c1.start();
    }
}

class CubbyHole {

    private int contents;
    private boolean available = false;

    public synchronized int get() {
        while (available == false) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        available = false;
        notifyAll();
        return contents;
    }

    public synchronized void put(int value) {
        while (available == true) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        contents = value;
        available = true;
        notifyAll();
    }
}

class Consumer extends Thread {

    private final CubbyHole cubbyhole;
    private final int number;

    public Consumer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            int value = cubbyhole.get();
            System.out.println("Consumer #" + this.number + " got: " + value);
        }
    }
}

class Producer extends Thread {

    private final CubbyHole cubbyhole;
    private final int number;

    public Producer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            cubbyhole.put(i);
            System.out.println("Producer #" + this.number + " put: " + i);
            try {
                sleep((int) (Math.random() * 100));
            } catch (InterruptedException e) {
            }
        }
    }
}

In this code both the threads calling different methods, i.e producer thread is only concerned about put method and Consumer thread only concerned with get method, so I was wondering why do we need to synchronize them when we have a loop inside to check for contents "while(available)"

Then I removed the synchronized keyword and it threw an IllegalStateMonitor Exception

So my question is do we need to have synchronized keyword only so that we can call notifyAll()

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
Nick Div
  • 5,338
  • 12
  • 65
  • 127

3 Answers3

1

Because both notifyAll and wait require the calling thread to own the monitor on the object the method was invoked on.

Throws:

IllegalMonitorStateException - if the current thread is not the owner of this object's monitor.

Since you're invoking notifyAll and wait on this, you'll need the monitor of this object. A synchronized instance method gives you that.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • I get it but looking at the code do we on logical grounds need to add synchronized keyword to the method declaration – Nick Div Jan 08 '15 at 16:44
  • @kapilchhattani You either need a `synchronized` method or you need to explicitly use `synchronized(this)`. – Sotirios Delimanolis Jan 08 '15 at 16:45
  • So if a method put() is synchronized does that mean that other thread will not be able to call even the get() method as well. I mean the synchronization is on the method so does that restrict other methods of the object getting called as well. – Nick Div Jan 08 '15 at 16:48
  • @kapil Perhaps you should read about [`synchronized`](http://stackoverflow.com/questions/1085709/what-does-synchronized-mean). – Sotirios Delimanolis Jan 08 '15 at 16:49
  • That link kind of cleared up a lot questions. Thanks. – Nick Div Jan 08 '15 at 16:56
  • Well I was thinking that a synchronized method puts only lock on a certain block of code not the object itself. And now I know why do we need to have synchronized in the method declarations in the code that I have posted. So I guess I am good for now.. – Nick Div Jan 08 '15 at 17:02
  • @kapilchhattani Careful with the word _lock_. `synchronized` doesn't lock an object. It locks the object's monitor. Only one thread can lock an object's monitor at a time. – Sotirios Delimanolis Jan 08 '15 at 17:05
1

Putting synchronized on the methods has a couple of effects:

  • it allows wait and notifyAll to work by having the 2 threads share the monitor that controls notifications. Calling notifyAll on an instance of CubbyHole notifies only threads that are waiting for the same CubbyHole instance.

  • it establishes memory visibility for the instance members of the CubbyHole object, so that once the thread has the monitor it is guaranteed to see the current values of these variables. The JVM can agressively cache values or reorder instructions, it is counting on having markers like the synchronized keyword to know what limits it should impose on these optimizations.

Putting synchronized on a method means that a thread needs to acquire the monitor for the instance that the method is called on before it can start executing the method. So the put and get methods on the cubbyhole object are using the same monitor, and if one thread is executing one of the methods the other thread is blocked from entering the other. Be aware that wait releases the thread's hold on the lock (allowing the other thread to work), the waiting thread has to reacquire the monitor before it can leave the wait.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • Thanks Nathan. I was a little bit confused how the synchronized keyword works. Your explanation and Sotirios's link cleared up the doubts. – Nick Div Jan 08 '15 at 17:04
  • @kapilchhattani "...it establises memory visibility..." That's a key concept. Requiring your program to hold a lock before you can `wait()` or `notify()` is the library author's subtle way of urging you to synchronize all access to whatever it is that the waiter is waiting for (i.e., the `contents` and `available` variables in your example). Virtually all inter-thread communication is through shared variables, and the Java Language Spec does not guarantee how that will work out if you don't use proper synchronization. – Solomon Slow Jan 08 '15 at 17:22
1

Synchronized keyword is used so that no two thread can execute the methods at the same time. Only one thread should be allowed to access put and get method. Thread which holds the lock for CubbyHole will be able to execute get and put method.

There will be error scenarios if you are not synchronizing get and put. Two scenarios for example-

  1. If available == false and two producer threads accessing put method simultaneously. Assume thread 1 assigns 10 to contents and thread 2 assign 20 and available == true. So what value consumer thread will get 10 or 20?

  2. If available == true and two consumer threads are accessing get method simultaneously. both will keep executing because get is not synchronized and both will get same value which is wrong.

Purpose of synchronizing the put method is that only one producer thread can produce while other producer threads shall wait until this produced value is be consumed by one consumer thread.

Purpose of synchronizing get method is that only one consumer thread can read the produced value while other consumer threads shall wait and then signal producer to produce by setting available as false.