Search code examples
javamultithreadingqueuereentrantlock

Producer Consumer using ReentrantLock and Queue


To understand how multi threading works I am using Queue and ReentrantLock to simulate Producer and Consumer problem. My Producer thread is adding data into the queue but Consumer is not removing. I am not sure if I have implemented it correctly.

producer class

package concurrency;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


class Producer implements Runnable{
    Queue<Integer> list;
    Condition con;
    ReentrantLock lock;
    int size;


    Producer(Queue q1, Condition con, ReentrantLock l1,int size)
    {
        this.list=q1;
        this.con=con;
        this.lock=l1;
        this.size=size;
    }

    public void run()
    {
        for(int i =0;i<20;i++)
        {
            if(lock.tryLock())
            {
                while(list.size()==size)
                {
                    try 
                    {
                        con.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                list.add(i);
                System.out.println("Producer "+ Thread.currentThread() +"added "+i+" to the List" );
                con.signalAll();
                lock.unlock();

            }
        }
    }
}

consumer class

class Consumer implements Runnable{
    Queue<Integer> list;
    Condition con;
    ReentrantLock lock;
    int size;

    Consumer(Queue q1, Condition con, ReentrantLock l1,int size)
    {
        this.list=q1;
        this.con=con;
        this.lock=l1;
        this.size=size;
    }

    public void run()
    {

        for(int innerLoop =0;innerLoop<20;innerLoop++){
            if(lock.tryLock()){
                while(list.size()<1){
                    try {
                        con.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                int i = (int) list.remove();
                System.out.println("Consumer "+ Thread.currentThread() +"removed "+i+"  from the List" );
                con.signalAll();
                lock.unlock();
            }
        }
    }
}

shared resource class

class SharedResource {

    Queue list ;
    
    Condition con;
    ReentrantLock lock;
    int size;

    SharedResource()
    {
        size =20;
        this.list=new LinkedList<Integer>();
        lock = new ReentrantLock();
        this.con = lock.newCondition();
    }
    
    public Queue getList() {
        return list;
    }

    public void setList(Queue list) {
        this.list = list;
    }

    public Condition getCon() {
        return con;
    }

    public void setCon(Condition con) {
        this.con = con;
    }

    public ReentrantLock getLock() {
        return lock;
    }

    public void setLock(ReentrantLock lock) {
        this.lock = lock;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

}

runnable class

public class MYPRODUCERCONSUMER {

    public static void main(String[] args) {

        SharedResource producerCObj = new SharedResource();
        Producer producer= new Producer(producerCObj.getList(), producerCObj.getCon(), producerCObj.getLock(), producerCObj.getSize());
        Thread producerThread= new Thread(producer);
        producerThread.start();

        Consumer consumer= new Consumer(producerCObj.getList(), producerCObj.getCon(), producerCObj.getLock(), producerCObj.getSize());
        Thread consumerThread= new Thread(consumer);
        consumerThread.start();
    }

}

Solution

  • In your consumer you try to acquire lock:

    if(lock.tryLock())
    

    But tryLock acquires the lock only if it is not held by another thread at the time of invocation. Just because you start producer first it is highly likely that it is already acquired by Producer. You try to do unlock but next instruction is tryLock (in a loop) so there is no any sort of yield for other thread. In other words Consumer thread almost has no chance to acquire lock because Producer thread reacquires it. And just because you have finite loop (only 20) your Consumer just finishes.

    If you add

    class Producer implements Runnable {
    
        Queue<Integer> list;
        Condition con;
        ReentrantLock lock;
        int size;
    
        Producer(Queue q1, Condition con, ReentrantLock l1,int size)
        {
            this.list=q1;
            this.con=con;
            this.lock=l1;
            this.size=size;
        }
    
        public void run()
        {
            for(int i =0;i<20;i++)
            {
                if(lock.tryLock())
                {
                    while(list.size()==size)
                    {
                        try
                        {
                            con.await();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    list.add(i);
                    System.out.println("Producer "+ Thread.currentThread() +"added "+i+" to the List" );
                    con.signalAll();
                    lock.unlock();
    
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    after

     lock.unlock();
    

    in your Producer loop you will give chance to Consumer thread to acquire lock and you will get result as expected.