0

I have implemented Classical example of producer and consumer. Here producer will sleep for 10 seconds after producing value = 0 [will not go waiting for the state because of queue size is one which is less than 10 ]. And the consumer will consume value =0 and notify the producer will sleep for one second.

So My question is that why Notify by the consumer is not interrupting producer thread and print Producer Exception cached.

The output of the following program is like:

Producer add value=0
Consumer consumes value=0

(wait for 10 seconds)

Producer add value=1
Consumer consumes value=1

(wait for 10 seconds)

Producer add value=2
Consumer consumes value=2

Classical Example of Producer and consumer.

public class ClassicalProducerConsumer{
    public static void main(String[] args) {
        Buffer  buffer = new Buffer(3);
        Thread producer = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    int value = 0;
                    while (true) {
                        buffer.add(value);
                        value++;
                        Thread.sleep(10000); // Make producer wait for 10 seconds.
                    }
                }catch (Exception ex){
                    System.out.println("Producer Exception cached");
                    ex.printStackTrace();
                }
            }
        });

        Thread consumer = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        int value = buffer.poll();
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    System.out.println("Consumer Exception cached");
                    e.printStackTrace();
                }
            }
        });
        producer.start();
        consumer.start();
    }
}

class Buffer{
    Queue<Integer> queue;
    int size;

    public Buffer(int size) {
        this.size = size;
        queue = new LinkedList<>();
    }
    public void add(int value) throws InterruptedException {
        synchronized (this){
            while (queue.size() >=size){
                wait();
            }
            System.out.println("Producer add value="+ value);
            queue.add(value);
            notify();
        }
    }
    public int poll() throws InterruptedException {
        synchronized (this){
            while (queue.size()==0){
                wait();
            }
            int value = queue.poll();
            System.out.println("Consumer consumes value="+ value);
            notify();
            return value;
        }
    }
}
Neelabh Singh
  • 2,600
  • 12
  • 51
  • 88
  • 3
    `notify` and `interrupt` are two different mechanisms. – RealSkeptic Dec 25 '18 at 17:29
  • Because it's possible to have multiple threads waiting on a producer/consumer problem like this, I usually prefer to use `notifyAll()` just to make sure any possible thread does not get starved or missed. – markspace Dec 25 '18 at 17:38

1 Answers1

0

As @RealSkeptic mentioned notify() has nothing to do with interruption. Call interrupt() on a thread you want to interrupt instead. Then any blocking method in this thread will return control by throwing Interrupted exception. It is advisable to restore the interrupted state of the thread by calling Thread.currentThread().interrupt() in the catch block.

Nick
  • 744
  • 6
  • 11
  • There's no need to restore the interrupted state. It's an oft-repeated piece of advice that serves no purpose. – John Kugelman Dec 26 '18 at 06:19
  • @JohnKugelman what if I need to examine whether the thread was interrupted or not later in the code? – Nick Dec 26 '18 at 06:23
  • @JohnKugelman Brian Goetz in his book "Concurrency in practice" says that there are two ways to deal with interruption - propagate it higher up the stack or restore it. Since Thread's 'run()' does not allow propagation we need to restore it then – Nick Dec 26 '18 at 06:32
  • You'll know based on whether you're in the `try` or the `catch` block. There's no need for a flag to tell you. If you catch an `SQLException` or a `NumberFormatException` you use code flow to tell whether you're in an exceptional state; you don't need to set or check a flag. – John Kugelman Dec 26 '18 at 06:33
  • The code you write is the only code that cares about the status. Once you've received the `InterruptedException` you've been notified of the interruption. There's nobody else you need to communicate that information to. The flag is only there for you in the event you are doing some long-running task without calling a blocking function like `sleep()`. There is no need to re-call `interrupt()`, nor is there any reason to call `isInterrupted()`. It is safe to handle `InterruptedException`s and/or check `interrupted()` and let the flag be cleared. – John Kugelman Dec 26 '18 at 06:40
  • @JohnKugelman ok, I see your point and agree with you but consider one particular scenario: the Thread's `run()` calls a method (let's call it `calculate()`) which should internally implement it's interruption policy and therefore catch Interrupted exception for any reasons (for instance: to log particular local variables in this method). Here `run()` knows nothing about interruption. But it might need to know. Of course you can rethrow Interrupted exception from the catch block of `calculate()` but restoring the interruption status can be a good way to go too – Nick Dec 26 '18 at 07:10
  • In that scenario I would document that `calculate()` is a slow/blocking method that can be interrupted in its Javadoc and would declare it with `throws InterruptedException`. – John Kugelman Dec 26 '18 at 12:55
  • 1
    I don't mean to be argumentative, and really my thoughts on interruptions deserve their own blog post, or maybe a standalone Q&A. I think the API documentation does a poor job of explaining how interrupts should be used and what they're for. You *can* preserve the interrupt flag and you *can* check it without clearing it, but neither are necessary. The API documentation ought to advocate for simply catching `InterruptedException` and in rare cases checking `interrupted()`. It should discourage calling `isInterrupted()`. Clearing the interrupt flag is normal and expected. – John Kugelman Dec 26 '18 at 13:00
  • 2
    Most importantly, it should explain who actually calls `interrupt()` and triggers interruptions: it's always your own code! Interruptions don't happen unless you yourself the application writer cause them to happen. If you don't call `interrupt()` yourself your threads will never be interrupted. It's an API flaw that `InterruptedException` is a checked exception. Being required to catch it any time we call `sleep()` is a mistake. Most of us don't interrupt our own threads; our sleep calls are never interrupted. We're required to handle exceptions that never happen! Ugh. – John Kugelman Dec 26 '18 at 13:03