2

I am trying to get familiar with wait() and notify() methods and I have written a simple class acting like a monitor for a simple producer-consumer excercise where there are N producers and N consumers. Anyway the monitor, as requested in the excercise, can store only 3 items. So the producers must wait; on the contrary if there are 0 items in the monitor the consumers must wait.

public class Monitor {

  private List<Integer> items;
  private int capacity;
  private Object waitProducer;
  private Object waitConsumer;
  private int counter;

  public Monitor() {
      this.items = new ArrayList<Integer>();
      this.capacity = 3;
      this.waitProducer = new Object();
      this.waitConsumer = new Object();
      this.counter = 0;
  }

  public void produce() throws InterruptedException {
      synchronized (this) {

          if (this.items.size() == this.capacity) {
              synchronized (this.waitProducer) {
                  System.out.println("Producer " +   Thread.currentThread().getId() + " aspetta");
                  this.waitProducer.wait(); /***/
              }
          }

          counter++;

          System.out.println("Thread " + Thread.currentThread().getId()
                + " produces object " + counter);           

          this.items.add(counter);
          synchronized (this.waitConsumer) {
            this.waitConsumer.notify();
          }

      }
  }

  public void consume() throws InterruptedException {
      synchronized (this) {
          if (this.items.size() == 0) {

              synchronized (this.waitConsumer) {
                  this.waitConsumer.wait(); /***/

              }
          }         

          System.out.println("Thread " + Thread.currentThread().getId()
                + " consume object " + this.items.get(0));

          this.items.remove(0);
          synchronized (this.waitProducer) {
              this.waitProducer.notify();
          }
      }
   }
}

I think that there is a problem in lines with /***/: infact when the wait() method is invoked then the thread releases the lock on waitProducer (or waitConsumer) but it does not on the Monitor object. That's why when the first produces (of the first consumer) calls wait() then the Monitor object is no more obtainable. Obvioudly the access to the Monitor object must be in mutual exclusion so that to update correctly the list and the counter. So, what is the correct way? Thanks

15
  • Why is the outer synchronized(this) there ? No other thread can enter it while one is waiting at this.waitProducer.wait(). Commented Feb 27, 2015 at 14:55
  • 1
    @LoryLory do a quick search on volatile vs synchronized on google Commented Feb 27, 2015 at 15:08
  • 2
    @LoryLory You should considering java.util.concurrent.Lock and use multiple Conditions. It would do what you want much easier. Commented Feb 27, 2015 at 15:12
  • 1
    @nafas Again they are not alternatives to each other. They provide very different services. Synchronization prevents race conditions and ensures that stability of the data within the variable or data structure. This can be considered execution control. Volatile has to do with the memory visibility where it prevents the data from being cached so it's always always the most up to date when accessed. This however, does not prevent a potential race condition. Commented Feb 27, 2015 at 15:22
  • 1
    @LoryLory Here's the JavaDoc for the Condition interface Commented Feb 27, 2015 at 15:53

2 Answers 2

3

Only one thread can execute the code inside synchronized blocks at a time.

Also, one must gain an exclusive lock on an object (via synchronized(object)) when calling wait() on that object.

This means, the unlocking/unblocking/notify code must be accessible to other threads outside of original block which called wait(). Otherwise, execution will wait forever.

In your code no other thread can reach this.waitConsumer.notify(); while one thread waits on this.waitProducer.wait();. Because all of it is also enclosed in a synchronized(this) block, other threads will keep contesting for locking on this.

Sign up to request clarification or add additional context in comments.

6 Comments

ok, more or less I have understood. I don't know how to put things together... because when a produced calls waitProduces.wait() it should also release the lock on the Monitor object...also the access to the Monitor must be synchronized...
@LoryLory wait() does not release a lock Yes it does. Or else, by definition, no thread would ever be able to notify.
Ok, I think that using Condition variables is the best solution. So, is there any other way only just by using wait() and notify() to get an equivalent solution?
I think the following code is what it resembles the most: pastebin.com/AwWH7XEA
Re, "Only one thread can execute inside synchronized block at a time" Not technically true! Two different threads can be in the same synchronized block of code at the same time if they are synchronized on different objects. That can happen by design, when the data to be protected is associated with the synchronization object, or it can be by mistake. E.g., when some noob writes synchronized (foo) { ...; foo=new Foo(); ... }
|
1

@james large What do you mean that two threads can be synchronized in the same block if they are synchronized on different objects?

Suppose you have a synchronized block that looks like this:

synchronized(obj) {
    ...body of synchronized block...
}
...whatever happens next...

When Java thread T executes that statement, it starts by evaluating the expression, obj. That expression must either return null, or it must return a reference to an object. If it's null, then a NullPointerException will be thrown. Otherwise, thread T will attempt to lock the object.

No two threads can ever lock the same object at the same time. If the object already was locked by some other thread, then thread T will be unable to proceed until the object is unlocked.

When thread T finally is able to lock the object, it will execute the body of the synchronized block, and then it will unlock the object and move on to whatever happens next.

The important concept here is the expression, obj. Suppose that thread T evaluates obj, gets a reference to object O, locks it, and enters the synchronized block. Then thread U comes along, evaluates obj, gets a different object, P. Thread U will lock object P, and will be able to enter the synchronized block while thread T still is in there at the same time.

So how can that happen?

When it's intended, it might look like this:

class MyClass {
    private final Object lock = new Object();

    void foobar() {
        synchronized(lock) { ... }
    }
}

If thread T and thread U are operating on different instances of MyClass, then each instance will have its own lock object. Usually, in this case, the body of the synchronized block will only operate on other instance variables of MyClass. Since the two threads are operating on different data, there is no harm in them both being in the same synchronized block at the same time.

The purpose of synchronization is not to keep two threads out of the same method at the same time: The purpose is to keep two threads from operating on the same data at the same time.


Sometimes a newbie programmer writes this:

static Integer count = new Integer(0);

...
synchronized(count) {
    count += 1;
    ...operate on static data...
}

This is almost always a mistake. When thread T enters the block, it will synchronize on the Integer object that is referenced by the static variable count. But then the next thing it does is update count to refer to a different Integer object. Then along comes thread U. U synchronizes on the new count, and both threads are in the same block, operating on the same static data when they should not.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.