javamultithreadingjava.util.concurrentblockingqueue

Preferable approach for waiting on empty BlockingQueue


I've got a multithreaded application with one thread putting items into a BlockingQueue and multiple threads taking items from it for procession. The question is about taking items from the queue, currently it's implemented like this:

class PriorityQueueWorker<T> implements Runnable {
  private final PriorityBlockingQueue<T> requestQueue;
  private final AtomicBoolean continueExecution;

  @Override
  public void run() {
    do {
      T request = null;
      try {
        request = requestQueue.take();
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        continueExecution.set(false);
      }

      if (request == null) {
        continue;
      }

      //... here we do the work

    } while (continueExecution.get());

}

According to the JavaDoc of BlockingQueue.take() it Retrieves and removes the head of this queue, waiting if necessary until an element becomes available and for PriorityBlockingQueue this means the thread will be blocked on PriorityBlockingQueue.take() until an item appears in the queue:

public E take() throws InterruptedException {
  final ReentrantLock lock = this.lock;
  lock.lockInterruptibly();
  E result;
  try {
    while ((result = dequeue()) == null)
      notEmpty.await();
  } finally {
    lock.unlock();
  }
  return result;
}

An alternative way to implement our logic is to use PriorityBlockingQueue.poll() which returns null in case of empty queue:

public E poll() {
  final ReentrantLock lock = this.lock;
  lock.lock();
  try {
      return dequeue();
  } finally {
      lock.unlock();
  }
}

As far as I understand, the difference about these approaches is that with take() threads are blocked on the empty queue waiting, and with poll() they go on spinning around the infinite loop in PriorityQueueWorker.run().

For me the use-case when the queue is empty for a long time is quite common, so my question is which approach is better as of performance point of view: keep threads blocked (take-approach) or keep them spinning (poll-approach)?


Solution

  • which approach is better as of performance point of view...?

    What does "performance" mean to you?

    A worker polling an empty queue will try to use 100% of a CPU core. That will limit the availability of that core to work on behalf of any other thread. It also consumes power, and produces heat. But, if the number of threads in your application is carefully matched to the number of CPU cores, then it could make your worker threads respond more quickly to new work items: When a work item arrives, chances are very good that some worker already is in-core and running and able to deal with it right away.

    In many (most?) applications, OTOH, there will be other threads in the application and other processes running on the box that all want to share the CPU cores. Using take() in that case frees up the CPU cores to do other things when your workers have nothing to do. That also is a kind of "performance." Plus, using take() means your application will consume less power (Big deal if your app could ever run on a battery-powered device), and it will generate less heat (could be an issue if your application is running in a data center, or in a hot environment.) The down-side of take() is that, when a new work item arrives, the OS will take some time to "wake up" a waiting worker thread so that it can start to process it.

    IMO: You should start with take(), and only try polling if you are sure (i.e., if you have measured the performance) that you have a problem, and you are sure (measured again) that polling fixes it. Also, don't try polling unless you can guarantee that the number of threads doing it is less than the number of available cores.