Search code examples
javamultithreadingparallel-processingwaitnotify

Thread isn't resuming execution after notify()


I have two classes (Customer and Till). Customer thread waits until it is notified by a till thread. In my program, the customer thread is not executing it's code after being notified by the till thread. The till thread continues it's execution.

Customer.java (Customer thread extends Thread)

import java.util.concurrent.*;
import java.util.*;

public class Customer extends Thread
{
    Random random_generator = new Random();

    public int minimumQueueLength;
    public Set set;
    public Iterator iterator;
    public boolean placed_in_queue;

    public List<Integer> queue_length_list;
    public CopyOnWriteArrayList till_set = new CopyOnWriteArrayList();
    public Till till, till_to_join;
    public final Object lock;

    public Customer(CopyOnWriteArrayList till_set)
    {
        this.till_set = till_set;
        this.placed_in_queue = false;
        queue_length_list = new ArrayList<Integer>();
        lock = new Object();
    }

    public void run()
    {   
        try 
        {
            place_in_queue();
        } 
        catch (InterruptedException e1)
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        if(placed_in_queue)
        {   
            synchronized(this.lock)
            {

                System.out.println(this.getName()+" waiting");

                try {
                    this.lock.wait();

                    System.out.println(this.getName()+" has been woken");

                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        }
        else
        {
        }
    }

    public void place_in_queue() throws InterruptedException
    {
        placed_in_queue = false;
        iterator = till_set.iterator();

        while(iterator.hasNext())
        {
            till = (Till)iterator.next();
            queue_length_list.add(till.customer_queue.size());
        } 

        minimumQueueLength = 
                queue_length_list.indexOf(Collections.min(queue_length_list));

        if(minimumQueueLength < 5)
        {
            try 
            {
                till_to_join = (Till)till_set.get(minimumQueueLength);
                till_to_join.customer_queue.put(this);
                placed_in_queue = true;
            } 
            catch (InterruptedException e) 
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

Till.java (till thread extends Thread)

import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.*;

public class Till extends Thread
{
    BlockingQueue<String> item_queue = new ArrayBlockingQueue<String>(200);
    BlockingQueue<Customer> customer_queue = new ArrayBlockingQueue<Customer>(10);

    public Random random;
    public Customer c;

    public Till(BlockingQueue<String> item_queue) throws InterruptedException
    {
        this.item_queue = item_queue;
        random = new Random();
    }

    public void run()
    {                   
        while(true)
        {   
            try 
            {
                c = customer_queue.take();

                synchronized(c.lock)
                {
                    System.out.println(this.getName()+" Waking up : "+c.getName());
                    c.lock.notify();
                    System.out.println(c.getName()+" has been notified!");
                }           
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
        }
    }
}

CustomerGenerator.java

import java.util.*;
import java.util.concurrent.*;

public class CustomerGenerator extends Thread
{
    public int customer_generation_rate;

    //0 - slow
    //1 - fast

    public Random random_generator;

    public static BlockingQueue<String> item_queue = new ArrayBlockingQueue<String>(200);
    public static CopyOnWriteArrayList till_set = new CopyOnWriteArrayList();

    public int i;

    public CustomerGenerator(int customer_generation_rate, CopyOnWriteArrayList till_set)
    {
        this.customer_generation_rate = customer_generation_rate;
        this.till_set = till_set;
        this.i = 0;
        random_generator = new Random();    
    }

    public void run()
    {
        while(i<1)
        {
            switch(customer_generation_rate)
            {         
            case 0 : try 
            {
                Thread.sleep(random_generator.nextInt(1000));
            } 
            catch (InterruptedException e) 
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;

            case 1 : try
            {
                Thread.sleep(random_generator.nextInt(500));
            }
            catch(InterruptedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;

            default : customer_generation_rate = 0;
            break;
            }

            Customer customer = new Customer(till_set);
            customer.start();
            total_customer_count++;
            i++;
        }
    }
} 

Driver.java

import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Driver
{
    public static BlockingQueue<String> item_queue = new ArrayBlockingQueue<>(200);

    public static CopyOnWriteArrayList<Till> till_set = new CopyOnWriteArrayList<Till>();

    public static Set set;
    public static Iterator iterator;

    public static int i;

    public static final int till_count = 5;

    public static Thread till_thread;

    public static Till till_object;

    public static ExecutorService till_service = Executors.newFixedThreadPool(5);

    public static void main(final String[] args) throws InterruptedException
    {

        for(i=0; i<till_count; i++)
        {
            till_object = new Till(item_queue);
            till_set.add(till_object);
        }

        final CustomerGenerator customer_generator = new CustomerGenerator(0, till_set);
        customer_generator.start();

        Thread.sleep(5000);

        for(final Till t : till_set)
        {
            till_service.submit(t);
        }
    }
}

Output Obtained:

Thread-7 waiting
Thread-1 Waking up : Thread-7
Thread-7 has been notified!

Expected Output:

Thread-7 waiting
Thread-1 Waking up : Thread-7
Thread-7 has been notified!
Thread-7 has been woken

Please help. Thank you. :)


Solution

  • CustomerGenerator generates one customer only when invoked. Making a mcve version of it makes it very clear:

    //i was initialized: i=0;
    public void run()
    {
        while(i<1)
        {
            final Customer customer = new Customer(till_set);
            customer.start();
            i++;
        }
    }
    

    I do not think that is what you meant.
    I find mcve a very useful technique. Not only it makes helping much easier, it is a powerful debugging tool. It many case, while preparing one, you are likely to find the problem. mcve should demonstrate the problem, and not your application.

    There may be other issues in the code. For more help please post Mcve.
    Some other comments:

    In CustomerGenerator you pass a reference of all tills to a Customer by: final Customer customer = new Customer(till_set); which is later used for selecting a till. I think till selection calculation would better be done in another class, say TillsManager which can have a stack of all customers waiting for a till.

    In Driver defining

     public static Till till_object; 
     for(i=0; i<5 ; i++)
     {
        till_object = new Till(item_queue);
        till_set.add(till_object);
     }
    

    means you will end up with 5 times the same object in till_set. I assume you wanted :

     for(i=0; i<till_count; i++)
     {
        Till till_object = new Till(item_queue);
        till_set.add(till_object);
    }