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.
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)
//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
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: []
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
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.
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.
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
I think there is a typo in the following statement:
ReplyDelete"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."
@Anonymous, That's Indeed typo. put() method blocks if BlockingQueue is full. Thanks for pointing it out.
ReplyDeleteHow 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
ReplyDeleteI 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.
ReplyDeleteQueue'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