views:

458

answers:

6

Can I get a complete simple scenario i.e. tutorial that suggest how this should be used, specifically with a Queue?

+1  A: 

http://www.javamex.com/tutorials/synchronization_producer_consumer_2.shtml

http://www.javamex.com/tutorials/synchronization_producer_consumer.shtml

http://www.java-samples.com/showtutorial.php?tutorialid=306

Calm Storm
Consider improving your answer by adding a description of your links, or a part of code which actually answers the question. Links are not eternal. For someone stumbling upon this question from a search engine, it will be more useful to find a concrete answer, than more links.
Gnoupi
For me the opposite is true. I like DRY. When I post links I think if there is *more* I can add to what is already said in the link. If I can't add more I simply point them and get out of the way.
Calm Storm
+2  A: 

Have you taken a look at this Java Tutorial?

Further, I'd advise you to stay the heck away from playing with this kind of stuff in real software. It's good to play with it so you know what it is, but concurrency has pitfalls all over the place. It's better to use higher level abstractions and synchronized collections or JMS queues if you are building software for other people.

That is at least what I do. I'm not a concurrency expert so I stay away from handling threads by hand wherever possible.

extraneon
+5  A: 

Even though you asked for wait() and notify() specifically, I feel that this quote is still important enough:

Josh Bloch, Effective Java 2nd Edition, Item 69: Prefer concurrency utilities to wait and notify (emphasis his):

Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead [...] using wait and notify directly is like programming in "concurrency assembly language", as compared to the higher-level language provided by java.util.concurrent. There is seldom, if ever, reason to use wait and notify in new code.

polygenelubricants
+1 For mentioning java.util.concurrent.
Helper Method
+2  A: 

Not a queue example, but extremely simple :)

class MyHouse {
    private boolean pizzaArrived = false;

    public void eatPizza(){
        synchronized(this){
            while(!pizzaArrived){
                wait();
            }
        }
        System.out.println("yumyum..");
    }

    public void pizzaGuy(){
        synchronized(this){
             this.pizzaArrived = true;
             notifyAll();
        }
    }
}

Some important points:
1) NEVER do

 if(!pizzaArrived){
     wait();
 }

Always use while(condition), because

  • a) threads can sporadically awake from waiting state without being notified by anyone. (even when the pizza guy didn't ring the chime, somebody would decide try eating the pizza. Don't know why it was designed so).
  • b) You should check for the condition again after acquiring the synchronized lock. Let's say pizza don't last forever. You awake, line-up for the pizza, but it's not enough for everybody. If you don't check, you might eat paper! :) (probably better example would be while(!pizzaExists){ wait(); }.

2) You must hold the lock (synchronized) before invoking wait/nofity (You'll get an exception if you try without holding a lock, so you will notice anyway though). Btw Threads also have to acquire lock before waking.

3) Avoid acquiring any lock within your synchronized block. Strive to not invoke alien methods (methods you don't know for sure what they are doing). If you have to, make sure to take measures to avoid dead locks.

4) Be careful with notify(). Stick with notifyAll() until you know what you are doing.

5)Last, but not least, buy Java Concurrency in Practice!

Enno Shioji
Could you please elaborate on why not to use "if( ! pizzaArrived ){ wait(); } " ?
Everyone
@Everyone: Added some explanation. HTH.
Enno Shioji
+1 for mentioning spurious wakeup
finnw
A: 

The wait() and notify() methods are designed to provide a mechanism to allow a thread to block until a specific condition is met. For this I assume you're wanting to write a blocking queue implementation, where you have some fixed size backing-store of elements.

The first thing you have to do is to identify the conditions that you want the methods to wait for. In this case, you will want the put() method to block until there is free space in the store, and you will want the take() method to block until there is some element to return.

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while(queue.size() == capacity) {
            wait();
        }

        queue.add(element);
        notify();
    }

    public synchronized T take() throws InterruptedException {
        while(queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify();
        return item;
    }
}

There are a few things to note about the way in which you must use the wait and notify mechanisms.

Firstly, you need to ensure that any calls to wait() or notify() are within a synchronized region of code (with the wait() and notify() calls being synchronized on the same object). The reason for this (other than the standard thread safety concerns) is due to something known as a missed signal.

An example of this, is that a thread may call put() when the queue happens to be full, it then checks the condition, sees that the queue is full, however before it can block another thread is scheduled. This second thread then take()'s an element from the queue, and notifies the waiting threads that the queue is no longer full. Because the first thread has already checked the condition however, it will simply call wait() after being re-scheduled, even though it could make progress.

By synchronizing on a shared object, you can ensure that this problem does not occur, as the second thread's take() call will not be able to make progress until the first thread has actually blocked.

Secondly, you need to put the condition you are checking in a while loop, rather than an if statement, due to a problem known as spurious wake-ups. This is where a waiting thread can sometimes be re-activated without notify() being called. Putting this check in a while loop will ensure that if a spurious wake-up occurs, the condition will be re-checked, and the thread will call wait() again.


As some of the other answers have mentioned, Java 1.5 introduced a new concurrency library (in the java.util.concurrent package) which was designed to provide a higher level abstraction over the wait/notify mechanism. Using these new features, you could rewrite the original example like so:

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(T element) throws InterruptedException {
        lock.lock();
        try {
            while(queue.size() == capacity) {
                notFull.await();
            }

            queue.add(element);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while(queue.isEmpty()) {
                notEmpty.await();
            }

            T item = queue.remove();
            notFull.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

Of course if you actually need a blocking queue, then you should use an implementation of the BlockingQueue interface.

Also, for stuff like this I'd highly recommend Java Concurrency in Practice, as it covers everything you could want to know about concurrency related problems and solutions.

Jared Russell
This implementation is deadlock-prone. You need a call to `notify` *inside* the loop in the definition of `take`, because of spurious wakeup. Or you could take the easy way out and use `notifyAll` instead of `notify`.
finnw
A: 

//can any1 please help me how to write wait(), notify() and synchronized in the code given //below

package cs230.a2.q1;

/** * BufferImplWithJavaPrimative * * @author Andrew Meads * * @param */ public class BufferImplWithJavaPrimitives implements Buffer {

// Buffer data structure variables.
private int fCapacity; // Number of elements that can be stored.
private int fFront; // Index of element at the front of the buffer.
private int fBack; // Index of next free slot within the buffer.
private T[] fElements; // Array to store elements.

/**
 * Creates a new {@link BufferImplWithSemaphore} that can hold the given
 * number of elements at a time.
 * 
 * @param capacity
 *            the number of elements able to be held at any given time.
 */
@SuppressWarnings("unchecked")
public BufferImplWithJavaPrimitives(int capacity) {
    if (capacity <= 0) {
        throw new IllegalArgumentException();
    }

    fCapacity = capacity;
    fElements = (T[]) new Object[fCapacity];
    fFront = 0;
    fBack = 0;

}

/**
 * Gets the next element in the buffer. Waits until there is at least one
 * element in the buffer before returning.
 * 
 * @return the next element in the buffer
 */
public T get() throws InterruptedException {

    // TODO: Wait until there are elements in the buffer
    /* Remove this line */ if (isEmpty()) throw new RuntimeException();

    T result = fElements[fFront];
    fElements[fFront] = null;
    fFront = (fFront + 1) % fCapacity;

    // TODO: Notify any waiting threads that we're done


    return result;
}

/**
 * Adds the given element to the buffer. Waits until there is space in the
 * buffer before adding it.
 * 
 * @param element
 *            the element to add.
 */
public void put(T element) throws InterruptedException {

    // TODO: Wait until there is space in the buffer
    /* Remove this line */ if (isFull()) throw new RuntimeException();

    fElements[fBack] = element;
    fBack = (fBack + 1) % fCapacity;

    // TODO: Notify any waiting threads that we're done
}

/**
 * Gets a value indicating whether the buffer is full.
 * 
 * @return true if the buffer is full, false otherwise.
 */
public boolean isFull() throws InterruptedException {

    // TODO: Maybe we need to do something here?

    boolean result = false;
    result = fFront == fBack && fElements[fFront] != null;
    return result;
}

/**
 * Gets a value indicating whether the buffer is empty.
 * 
 * @return true if the buffer is empty, false otherwise.
 */
public boolean isEmpty() throws InterruptedException {

    // TODO: Maybe we need to do something here?

    boolean result = false;
    result = fFront == fBack && fElements[fFront] == null;
    return result;
}

}

mexico