Friday, August 13, 2021

How to use BlockingQueue in Java? ArrayBlockingQueue and LinkedBlockingQueue Example Tutorial

Hello Java Programmers, if you have ever implemented a classical producer-consumer problem in Java then there is a great chance that you may have heard about BlockingQueue class from the Java concurrency package. It's a very useful class to implement inter-thread communication in Java without you having to write low-level wait and notify code.  BlockingQueue in Java is added in Java 1.5 along with various other concurrent Utility classes like ConcurrentHashMap, Counting Semaphore, CopyOnWriteArrrayList etc.

BlockingQueue is a unique collection type that not only store elements but also supports flow control by introducing blocking if either BlockingQueue is full or empty. take() method of BlockingQueue will block if Queue is empty and put() method of BlockingQueue will block if Queue is full. 

This property makes BlockingQueue an ideal choice for implementing Producer consumer design pattern where one thread insert an element into BlockingQueue and another thread consumes it.

In this Java tutorial, we will learn about What is BlockingQueue in Java, How to use BlockingQueue, ArrayBlockingQueue and LinkedBlockingQueue and some important properties of it.

Important properties of BlockingQueue in Java

Before using any new Collection class e.g. BlockingQueue, I always read their API documentation to know more about it. There are always some important points which is worth remembering and avoids potential programming errors while using the new Collection class. Following a list of points about BlockingQueue in Java will help to learn and understand more about it.

1) BlockingQueue in Java doesn't allow null elements, various implementation of BlockingQueue like ArrayBlockingQueue, LinkedBlockingQueue throws NullPointerException when you try to add null on queue.



BlockingQueue<String> bQueue = new ArrayBlockingQueue<String>(10);
//bQueue.put(null); //NullPointerException - BlockingQueue in Java doesn't allow null
     
bQueue = new LinkedBlockingQueue<String>();
bQueue.put(null);

Exception in thread "main" java.lang.NullPointerException
        at java.util.concurrent.LinkedBlockingQueue.put(LinkedBlockingQueue.java:288)


2) BlockingQueue can be bounded or unbounded. A bounded BlockingQueue is one that is initialized with the initial capacity and calls to put() will be blocked if BlockingQueue is full and size is equal to capacity. This bounding nature makes it ideal to use a shared queue between multiple threads like in most common Producer consumer solutions in Java

An unbounded Queue is one that is initialized without capacity, actually by default it is initialized with Integer.MAX_VALUE

The most common example of BlockingQueue uses bounded BlockingQueue as shown in the below example.

BlockingQueue<String> bQueue = new ArrayBlockingQueue<String>(2);
bQueue.put("Java");
System.out.println("Item 1 inserted into BlockingQueue");
bQueue.put("JDK");
System.out.println("Item 2 is inserted on BlockingQueue");
bQueue.put("J2SE");
System.out.println("Done");

Output:
Item 1 inserted into BlockingQueue
Item 2 is inserted on BlockingQueue


This code will only insert Java and JDK into BlockingQueue and then it will block while inserting 3rd element J2SE because the size of BlockingQueue is 2 here.

3. BlockingQueue implementations like ArrayBlockingQueue, LinkedBlockingQueue, and PriorityBlockingQueue are thread-safe. All queuing method uses concurrency control and internal locks to perform operation atomically. 

Since BlockingQueue also extends Collection, bulk Collection operations like addAll(), containsAll() are not performed atomically until any BlockingQueue implementation specifically supports it. So call to addAll() may fail after inserting a couple of elements.

4) Common methods of BlockingQueue is are put() and take() which are blocking methods in Java and used to insert and retrieve elements from BlockingQueue in Java. put() will block if BlockingQueue is full and take() will block if BlockingQueue is empty, call to take() removes element from the head of Queue as shown in the following example:

BlockingQueue<String> bQueue = new ArrayBlockingQueue<String>(2);
bQueue.put("Java"); //insert object into BlockingQueue
System.out.println("BlockingQueue after put: " + bQueue);
bQueue.take(); //retrieve object from BlockingQueue in Java
System.out.println("BlockingQueue after take: " + bQueue);

Output:
BlockingQueue after put: [Java]
BlockingQueue after take: []


5) BlockingQueue interface extends Collection, Queue, and Iterable interface which provides it all Collection and Queue related methods like poll(), and peek(), unlike take(), peek() method returns head of the queue without removing it, poll() also retrieves and removes elements from the head but can wait till specified time if Queue is empty.

BlockingQueue<String> linkedBQueue = new LinkedBlockingQueue<String>(2);
linkedBQueue.put("Java"); //puts object into BlockingQueue
System.out.println("size of BlockingQueue before peek : " + linkedBQueue.size());      
System.out.println("example of peek() in BlockingQueue: " + linkedBQueue.peek());
System.out.println("size of BlockingQueue after peek : " + linkedBQueue.size());
System.out.println("calling poll() on BlockingQueue: " + linkedBQueue.poll());
System.out.println("size of BlockingQueue after poll : " + linkedBQueue.size());

Output:
size of BlockingQueue before peek : 1
example of peek() in BlockingQueue: Java
size of BlockingQueue after peek : 1
calling poll() on BlockingQueue: Java
size of BlockingQueue after poll : 0


6) Another important method from BlockingQueue in Java is remainingCapacity() and offer(), former returns number remaining space in BlockingQueue, which can be filled without blocking while later insert an object into the queue if possible and return true if success and false if fail unlike add() method which throws IllegalStateException if it fails to insert an object into BlockingQueue. Use offer() over add() wherever possible.





Usage of BlockingQueue in Java

There can be many creative usages of BlockingQueue in Java given its flow control ability. Two of the most common ways I see programmer uses BlockingQueue is to implement Producer Consumer design pattern and implementing Bounded buffer in Java. It surprisingly made coding and inter-thread communication over a shared object very easy.

BlockingQueue in Java – ArrayBlockingQueue vs LinkedBlockingQueue Example



ArrayBlockingQueue and LinkedBlockingQueue in Java

ArrayBlockingQueue and LinkedBlockingQueue are common implementation of BlockingQueue<E> interface. ArrayBlockingQueue is backed by an array  and Queue imposes orders as FIFO. 

head of the queue is the oldest element in terms of time and tail of the queue is the youngest element. ArrayBlockingQueue is also a fixed size bounded buffer on the other hand LinkedBlockingQueue is an optionally bounded queue built on top of Linked nodes. In terms of throughput, LinkedBlockingQueue provides higher throughput than ArrayBlockingQueue in Java.


That’s all on What is BlockingQueue in Java and How to use it. We have seen two convenient implementations of BlockingQueue i.e. ArrayBlockingQueue and LinkedBlockingQueue which comes along with Java API.

If you are implementing Producer Consumer design pattern in Java, consider using BlockingQueue, it not only make coding easy but also performs better and provide better robustness and stability than writing your own BlockingQueue or using naked wait and notify method.



Other Java Collection tutorials for Java developers

5 comments:

  1. I think there is a typo in the following statement:

    "take() method of BlockingQueue will block if Queue is empty and put() method of BlockingQueue will block if Queue is empty."

    should be "put() method of BlockingQueue will block if Queue is full."


    ReplyDelete
  2. @Anonymous, That's Indeed typo. put() method blocks if BlockingQueue is full. Thanks for pointing it out.

    ReplyDelete
  3. How do you know if BlockingQueue is thread-safe and doesn't allow null elements? Did you ran any test?? Also it's worth mentioning that take() remove objects from head of the queue

    ReplyDelete
  4. I think most important difference between ArrayBlockingQueue and LinkedBlockingQueue is that former is bounded and later is unbounded. So use former, if you need bounded queue functionality otherwise simply use LinkedBlockingQueue.

    ReplyDelete
  5. Queue's can be major source of latency in multi-threading environment. Extensive use of Queue in low latency application can jeopardize your performance goal, as there is always contention. Minimize sharing data between threads, that should be end goal.

    ReplyDelete