Blocking queue and multi-threaded consumer, how to know when to stop

JavaMultithreadingProducer ConsumerBlockingqueue

Java Problem Overview


I have a single thread producer which creates some task objects which are then added into an ArrayBlockingQueue (which is of fixed size).

I also start a multi-threaded consumer. This is build as a fixed thread pool (Executors.newFixedThreadPool(threadCount);). I then submit some ConsumerWorker intances to this threadPool, each ConsumerWorker having a refference to the above mentioned ArrayBlockingQueue instance.

Each such Worker will do a take() on the queue and deal with the task.

My issue is, what's the best way to have a Worker know when there won't be any more work to be done. In other words, how do I tell the Workers that the producer has finished adding to the queue, and from this point on, each worker should stop when he sees that the Queue is empty.

What I've got now is a setup where my Producer is initialized with a callback which is triggered when he finishes it's job (of adding stuff to the queue). I also keep a list of all the ConsumerWorkers I've created and submitted to the ThreadPool. When the Producer Callback tells me that the producer is done, I can tell this to each of the workers. At this point they should simply keep checking if the queue is not empty, and when it becomes empty they should stop, thus allowing me to gracefully shutDown the ExecutorService thread pool. It's something like this

public class ConsumerWorker implements Runnable{

private BlockingQueue<Produced> inputQueue;
private volatile boolean isRunning = true;

public ConsumerWorker(BlockingQueue<Produced> inputQueue) {
	this.inputQueue = inputQueue;
}

@Override
public void run() {
	//worker loop keeps taking en element from the queue as long as the producer is still running or as 
	//long as the queue is not empty:
	while(isRunning || !inputQueue.isEmpty()) {
		System.out.println("Consumer "+Thread.currentThread().getName()+" START");
		try {
			Object queueElement = inputQueue.take();
			//process queueElement
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

//this is used to signal from the main thread that he producer has finished adding stuff to the queue
public void setRunning(boolean isRunning) {
	this.isRunning = isRunning;
}

}

The problem here is that I have an obvious race condition where sometimes the producer will finish, signal it, and the ConsumerWorkers will stop BEFORE consuming everything in the queue.

My question is what's the best way to synchronize this so that it all works ok? Should I synchronize the whole part where it checks if the producer is running plus if the queue is empty plus take something from the queue in one block (on the queue object)? Should I just synchronize the update of the isRunning boolean on the ConsumerWorker instance? Any other suggestion?

UPDATE, HERE'S THE WORKING IMPLEMENTATION THAT I'VE ENDED UP USING:

public class ConsumerWorker implements Runnable{

private BlockingQueue<Produced> inputQueue;

private final static Produced POISON = new Produced(-1); 

public ConsumerWorker(BlockingQueue<Produced> inputQueue) {
	this.inputQueue = inputQueue;
}

@Override
public void run() {
	//worker loop keeps taking en element from the queue as long as the producer is still running or as 
	//long as the queue is not empty:
	while(true) {
		System.out.println("Consumer "+Thread.currentThread().getName()+" START");
		try {
			Produced queueElement = inputQueue.take();
			Thread.sleep(new Random().nextInt(100));
			if(queueElement==POISON) {
				break;
			}
			//process queueElement
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("Consumer "+Thread.currentThread().getName()+" END");
	}
}

//this is used to signal from the main thread that he producer has finished adding stuff to the queue
public void stopRunning() {
	try {
		inputQueue.put(POISON);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

}

This was inspired heavily by JohnVint's answer below, with only some minor modifications.

=== Update due to @vendhan's comment.

Thank you for your obeservation. You are right, the first snippet of code in this question has (amongst other issues) the one where the while(isRunning || !inputQueue.isEmpty()) doesn't really make sense.

In my actual final implementation of this, I do something which is closer to your suggestion of replacing "||" (or) with "&&" (and), in the sense that each worker (consumer) now only checks if the element he's got from the list is a poison pill, and if so stops (so theoretically we can say that the worker has to be running AND the queue must not be empty).

Java Solutions


Solution 1 - Java

You should continue to take() from the queue. You can use a poison pill to tell the worker to stop. For example:

private final Object POISON_PILL = new Object();

@Override
public void run() {
    //worker loop keeps taking en element from the queue as long as the producer is still running or as 
    //long as the queue is not empty:
    while(isRunning) {
        System.out.println("Consumer "+Thread.currentThread().getName()+" START");
        try {
            Object queueElement = inputQueue.take();
            if(queueElement == POISON_PILL) {
                 inputQueue.add(POISON_PILL);//notify other threads to stop
                 return;
            }
            //process queueElement
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//this is used to signal from the main thread that he producer has finished adding stuff to the queue
public void finish() {
    //you can also clear here if you wanted
    isRunning = false;
    inputQueue.add(POISON_PILL);
}

Solution 2 - Java

I'd send the workers a special work packet to signal that they should shut down:

public class ConsumerWorker implements Runnable{

private static final Produced DONE = new Produced();

private BlockingQueue<Produced> inputQueue;

public ConsumerWorker(BlockingQueue<Produced> inputQueue) {
    this.inputQueue = inputQueue;
}

@Override
public void run() {
    for (;;) {
        try {
            Produced item = inputQueue.take();
            if (item == DONE) {
                inputQueue.add(item); // keep in the queue so all workers stop
                break;
            }
            // process `item`
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

}

To stop the workers, simply add ConsumerWorker.DONE to the queue.

Solution 3 - Java

In your code-block where you attempt to retrive element from the queue , use poll(time,unit) instead of the take().

try { 
    Object queueElement = inputQueue.poll(timeout,unit);
     //process queueElement        
 } catch (InterruptedException e) {
        if(!isRunning && queue.isEmpty())
         return ; 
 } 

By specifying appropriate values of timeout , you ensure that threads wont keep blocking in case there is a unfortunate sequence of

  1. isRunning is true
  2. Queue becomes empty , so threads enter blocked wait ( if using take()
  3. isRunning is set to false

Solution 4 - Java

Can not we do it using a CountDownLatch, where the size is the number of records in the producer. And every consumer will countDown after process a record. And its crosses the awaits() method when all tasks finished. Then stop all ur consumers. As all records are processed.

Solution 5 - Java

There are a number of strategies you could use, but one simple one is to have a subclass of task that signals the end of the job. The producer doesn't send this signal directly. Instead, it enqueues an instance of this task subclass. When one of your consumers pulls off this task and executes it, that causes the signal to be sent.

Solution 6 - Java

I had to use a multi-threaded producer and a multi-threaded consumer. I ended up with a Scheduler -- N Producers -- M Consumers scheme, each two communicate via a queue (two queues total). The Scheduler fills the first queue with requests to produce data, and then fills it with N "poison pills". There is a counter of active producers (atomic int), and the last producer that receives the last poison pill sends M poison pills to the consumer queue.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionShivan DragonView Question on Stackoverflow
Solution 1 - JavaJohn VintView Answer on Stackoverflow
Solution 2 - JavaNPEView Answer on Stackoverflow
Solution 3 - JavaBhaskarView Answer on Stackoverflow
Solution 4 - JavaArpan DasView Answer on Stackoverflow
Solution 5 - JavaKaelin ColclasureView Answer on Stackoverflow
Solution 6 - Java18446744073709551615View Answer on Stackoverflow