changlamanus
changlamanus

Reputation: 21

Threads producer consumer in java

Below is the consumer producer problem code, but the code is not working as expected. Here the consumer and producer are supposed to be just producing and consuming one object.

public class ProducerConsumer {
    private static LinkedList<Integer> linkedList = new LinkedList<>();

    public static void main(String a[]) throws InterruptedException {
        Thread producer = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized(this) {
                    while (linkedList.size() == 1) {
                        try {
                            wait();
                        } catch(InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Produced");
                    linkedList.add(1);
                    notify();
                    try {
                        Thread.sleep(1000);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread consume = new Thread(new Runnable() {
            @Override
            public void run() {
                // produce
                synchronized(this) {
                    while (linkedList.isEmpty()) {
                        try {
                            wait();
                        } catch(InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Consumed");
                    linkedList.removeFirst();
                    notify();
                    try {
                        Thread.sleep(1000);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        producer.start();
        consume.start();
        producer.join();
        consume.join();

    }
}

We get the output as : Produced

And the program hangs.

Please help with possible solutions/ explanations

Upvotes: 1

Views: 954

Answers (3)

Grigoriev Nick
Grigoriev Nick

Reputation: 1129

I think best what you can do, is use BlockingQueue.

Upvotes: 0

Nathan Hughes
Nathan Hughes

Reputation: 96385

Use a shared lock. In the posted code each Runnable is using itself as a lock so no actual locking takes place.

When a thread waits, another thread needs to call notify on the same lock in order to wake up the waiting thread. We know from your logging that the Producer thread does its thing, but since the notify acts on a lock that is not the same as the one the Consumer is using, the consumer thread never wakes up.

Changing the code to use a shared lock works:

import java.util.*;

public class ProducerConsumer { private static LinkedList linkedList = new LinkedList();

public static void main(String a[]) throws InterruptedException {
    final Object lock = new Object();
    Thread producer = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (lock) {
                while (linkedList.size() ==1) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Produced");
                linkedList.add(1);
                lock.notify();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread consume = new Thread(new Runnable() {
        @Override
        public void run() {
            // produce
            synchronized (lock) {
                while (linkedList.isEmpty()) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Consumed");
                linkedList.removeFirst();
                lock.notify();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    producer.start();
    consume.start();
    producer.join();
    consume.join();

}

}

Output for this is:

c:\example>java ProducerConsumer
Produced
Consumed

which I think is what you're expecting.

Btw see this other answer I wrote for a dirt-simple implementation of a queue; you are better off protecting the shared data structure than putting the code in the threads accessing the data structure, especially look at how much easier the code is to write.

Upvotes: 2

Emmanuel H
Emmanuel H

Reputation: 92

Concurrency means that you can not know before runtime which Thread will end first. So you can not know which of the Consumer and Producer is launched, executed or finished first.

To help you, you can use a cyclic barrier https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html or applying the Fork/Join Framework https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html

Your synchronized blocs just say : only one Thread at a time can execute this part of code, not execute the first and the second after.

An example of how CyclicBarrier works :

service = Executors.newFixedThreadPool(numThreadsTotal);
CyclicBarrier c = new CyclicBarrier(numThreadsToWait);
runProducer();
c.await();
runConsumer();

It will wait until the there is as much Threads as numThreadsToWait that have execute the runProducer to execute the runConsumer().

Perhaps using a Thread Pool with a size of 1 could help you, but you will loose the benefits of concurrency.

Upvotes: 0

Related Questions