Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the use case for unbounded queue in Java Executors?

Executors factory from Java uses unbounded pending tasks queue. For instance, Executors.newFixedThreadPool uses new LinkedBlockingQueue which has not limit for tasks to be accepted.

public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
}

When new task arrives, and there is no thread available it goes to the queue. Tasks can be added to the queue indefinitely causing OutOfMemoryError.

What is the scenario for using this approach actually? Why Java creators didn't use bounded queue? I can't imagine a scenario when unbounded is better the bounded, but I may be missing something. Can someone provide a decent explanation? Best!

like image 684
Dariusz Mydlarz Avatar asked Dec 24 '18 12:12

Dariusz Mydlarz


People also ask

What is unbounded queue in Java?

Unbounded Queues are queues which are NOT bounded by capacity that means we should not provide the size of the queue. For example LinkedList (see previous example). All Queues which are available in java. util package are Unbounded Queues and Queues which are available in java.

What is the use of executors in Java?

An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. An Executor is normally used instead of explicitly creating threads.

What is the use of ThreadPoolExecutor?

ThreadPoolExecutor is an ExecutorService to execute each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods. It also provides various utility methods to check current threads statistics and control them.

What is role of a BlockingQueue in Executor framework?

The role of the BlockingQueue is to complete the Producer/Consumer pattern and if it's large enough, then you shouldn't see the issues you're encountering. As I mentioned above, you need to increase the queue size and catch the exception then retry executing the task.


2 Answers

This is the default approach and the user can choose to change to a bounded queue.

Now maybe your question is why is this the default?

It is actually harder to deal with bounded queues, what would you do if the queue is full? You drop the task and don't accept it? You throw an exception and fail the entire process? Isn't that what would happen in the case OOM? So all these are decision need to be taken by the user whose accepting lots of long running tasks, which is not the the default Java user.

A use case for unbounded queue could simply be when you only expect a small number of running concurrent requests but you don't know exactly how much or you can implement back pressure in a different stage of your application like throttling your API requests.

like image 79
Sleiman Jneidi Avatar answered Nov 10 '22 14:11

Sleiman Jneidi


You can reject tasks by using ArrayBlockingQueue (bounded blocking queue)

final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
executorService = new ThreadPoolExecutor(n, n,
    0L, TimeUnit.MILLISECONDS,
    queue);

Code above is equivalent to Executors.newFixedThreadPool(n), however instead of default unlimited LinkedBlockingQueue we use ArrayBlockingQueue with fixed capacity of 100. This means that if 100 tasks are already queued (and n being executed), new task will be rejected with RejectedExecutionException.

like image 33
user7294900 Avatar answered Nov 10 '22 13:11

user7294900