ArrayBlockingQueue
DelayQueue
LinkedBlockingQueue
PriorityBlockingQueue
SynchronousQueue
LinkedBlockingDeque
The BlockingQueue
interface in the java.util.concurrent
class represents a queue which is thread safe to put into, and take instances from. In this text I will show you how to use this BlockingQueue
.
BlockingQueue Usage
A BlockingQueue
is typically used to have on thread produce objects, which another thread consumes. Here is a diagram that illustrates this principle:
The producing thread will keep producing new objects and insert them into the queue, until the queue reaches some upper bound on what it can contain. It's limit, in other words. If the blocking queue reaches its upper limit, the producing thread is blocked while trying to insert the new object. It remains blocked until a consuming thread takes an object out of the queue.
The consuming thread keeps taking objects out of the blocking queue, and processes them. If the consuming thread tries to take an object out of an empty queue, the consuming thread is blocked until a producing thread puts an object into the queue.
BlockingQueue Methods
A BlockingQueue
has 4 different sets of methods for inserting, removing and examining the elements in the queue. Each set of methods behaves differently in case the requested operation cannot be carried out immediately. Here is a table of the methods:
Throws Exception | Special Value | Blocks | Times Out | |
Insert | add(o) |
offer(o) |
put(o) |
offer(o, timeout, timeunit) |
Remove | remove(o) |
poll(o) |
take(o) |
poll(timeout, timeunit) |
Examine | element(o) |
peek(o) |
|
|
The 4 different sets of behaviour means this:
It is not possible to insert null
into a BlockingQueue
. If you try to insert null, the BlockingQueue
will throw a NullPointerException
.
It is also possible to access all the elements inside a BlockingQueue
, and not just the elements at the start and end. For instance, say you have queued an object for processing, but your application decides to cancel it. You can then call e.g. remove(o)
to remove a specific object in the queue. However, this is not done very efficiently, so you should not use these Collection
methods unless you really have to.
BlockingQueue Implementations
Since BlockingQueue
is an interface, you need to use one of its implementations to use it. Thejava.util.concurrent
package has the following implementations of the BlockingQueue
interface (in Java 6):
ArrayBlockingQueue
ArrayBlockingQueue
is a bounded, blocking queue that stores the elements internally in an array. That it is bounded means that it cannot store unlimited amounts of elements. There is an upper bound on the number of elements it can store at the same time. You set the upper bond at instantiation time, and after that it cannot be changed.
The ArrayBlockingQueue
stores the elements internally in FIFO (First In, First Out) order. The head
of the queue is the element which has been in queue the longest time, and the tail
of the queue is the element which has been in the queue the shortest time.
Here is how to instantiate and use an ArrayBlockingQueue
:
BlockingQueue queue = new ArrayBlockingQueue(1024); queue.put("1"); Object object = queue.take();
Here is a BlockingQueue
example that uses Java Generics. Notice how you can put and take String's instead of :
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024); queue.put("1"); String string = queue.take();
DelayQueue
The DelayQueue
keeps the elements internally until a certain delay has expired. The elements must implement the interface java.util.concurrent.Delayed
. Here is how the interface looks:
public interface Delayed extends Comparable<Delayed< { public long getDelay(TimeUnit timeUnit); }
The value returned by the getDelay()
method should be the delay remaining before this element can be released. If 0 or a negative value is returned, the delay will be considered expired, and the element released at the next take()
etc. call on the DelayQueue
.
The TimeUnit
instance passed to the getDelay()
method is an Enum
that tells which time unit the delay should be returned in. The TimeUnit
enum can take these values:
DAYS HOURS MINUTES SECONDS MILLISECONDS MICROSECONDS NANOSECONDS
The Delayed
interface also extends the java.lang.Comparable
interface, as you can see, which means that Delayed
objects can be compared to each other. This is probably used internally in theDelayQueue
to order the elements in the queue, so they are released ordered by their expiration time.
Here is an example of how to use the DelayQueue
:
public class DelayQueueExample { public static void main(String[] args) { DelayQueue queue = new DelayQueue(); Delayed element1 = new DelayedElement(); queue.put(element1); Delayed element2 = queue.take(); } }
The DelayedElement
is an implementation of the Delayed
interface that I have created. It is not part of the java.util.concurrent
package. You will have to create your own implementation of theDelayed
interface to use the DelayQueue
class.
LinkedBlockingQueue
The LinkedBlockingQueue
keeps the elements internally in a linked structure (linked nodes). This linked structure can optionally have an upper bound if desired. If no upper bound is specified,Integer.MAX_VALUE
is used as the upper bound.
The LinkedBlockingQueue
stores the elements internally in FIFO (First In, First Out) order. The head
of the queue is the element which has been in queue the longest time, and the tail
of the queue is the element which has been in the queue the shortest time.
Here is how to instantiate and use a LinkedBlockingQueue
:
BlockingQueue<String> unbounded = new LinkedBlockingQueue<String>(); BlockingQueue<String> bounded = new LinkedBlockingQueue<String>(1024); bounded.put("Value"); String value = bounded.take();
PriorityBlockingQueue
The PriorityBlockingQueue
is an unbounded concurrent queue. It uses the same ordering rules as the java.util.PriorityQueue
class. You cannot insert null into this queue.
All elements inserted into the PriorityBlockingQueue
must implement the java.lang.Comparable
interface. The elements thus order themselves according to whatever priority you decide in yourComparable
implementation.
Notice that the PriorityBlockingQueue
does not enforce any specific behaviour for elements that have equal priority (compare() == 0).
Also notice, that in case you obtain an Iterator
from a PriorityBlockingQueue
, the Iterator
does not guarantee to iterate the elements in priority order.
Here is an example of how to use the PriorityBlockingQueue
:
BlockingQueue queue = new PriorityBlockingQueue(); //String implements java.lang.Comparable queue.put("Value"); String value = queue.take();
SynchronousQueue
The SynchronousQueue
is a queue that can only contain a single element internally. A thread inseting an element into the queue is blocked until another thread takes that element from the queue. Likewise, if a thread tries to take an element and no element is currently present, that thread is blocked until a thread insert an element into the queue.
Calling this class a queue is a bit of an overstatement. It's more of a rendesvouz point.
BlockingQueue Code Example
Here is an example of how to use a BlockingQueue
. The example uses the ArrayBlockingQueue
implementation of the BlockingQueue
interface.
First, the BlockingQueueExample
class which starts a Producer
and a Consumer
in separate threads. The Producer
inserts strings into a shared BlockingQueue
, and the Consumer
takes them out.
public class BlockingQueueExample { public static void main(String[] args) throws Exception { BlockingQueue queue = new ArrayBlockingQueue(1024); Producer producer = new Producer(queue); Consumer consumer = new Consumer(queue); new Thread(producer).start(); new Thread(consumer).start(); Thread.sleep(4000); } }
Here is the Producer
class. Notice how it sleeps a second between each put()
call. This will cause the Consumer
to block, while waiting for objects in the queue.
public class Producer implements Runnable{ protected BlockingQueue queue = null; public Producer(BlockingQueue queue) { this.queue = queue; } public void run() { try { queue.put("1"); Thread.sleep(1000); queue.put("2"); Thread.sleep(1000); queue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } } }
Here is the Consumer
class. It just takes out the objects from the queue, and prints them toSystem.out
.
public class Consumer implements Runnable{ protected BlockingQueue queue = null; public Consumer(BlockingQueue queue) { this.queue = queue; } public void run() { try { System.out.println(queue.take()); System.out.println(queue.take()); System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }
The BlockingDeque
interface in the java.util.concurrent
class represents a deque which is thread safe to put into, and take instances from.
The BlockingDeque
class is a Deque
which blocks threads tring to insert or remove elements from the deque, in case it is either not possible to insert or remove elements from the deque.
A deque
is short for "Double Ended Queue". Thus, a deque
is a queue which you can insert and take elements from, from both ends.
BlockingDeque Usage
A BlockingDeque
could be used if threads are both producing and consuming elements of the same queue. It could also just be used if the producting thread needs to insert at both ends of the queue, and the consuming thread needs to remove from both ends of the queue. Here is an illustration of that:
A BlockingDeque - threads can put and take from both ends of the deque. |
A thread will produce elements and insert them into either end of the queue. If the deque is currently full, the inserting thread will be blocked until a removing thread takes an element out of the deque. If the deque is currently empty, a removing thread will be blocked until an inserting thread inserts an element into the deque.
BlockingDeque methods
A BlockingDeque
has 4 different sets of methods for inserting, removing and examining the elements in the deque. Each set of methods behaves differently in case the requested operation cannot be carried out immediately. Here is a table of the methods:
Throws Exception | Special Value | Blocks | Times Out | |
Insert | addFirst(o) |
offerFirst(o) |
putFirst(o) |
offerFirst(o, timeout, timeunit) |
Remove | removeFirst(o) |
pollFirst(o) |
takeFirst(o) |
pollFirst(timeout, timeunit) |
Examine | getFirst(o) |
peekFirst(o) |
|
|
Throws Exception | Special Value | Blocks | Times Out | |
Insert | addLast(o) |
offerLast(o) |
putLast(o) |
offerLast(o, timeout, timeunit) |
Remove | removeLast(o) |
pollLast(o) |
takeLast(o) |
pollLast(timeout, timeunit) |
Examine | getLast(o) |
peekLast(o) |
|
|
The 4 different sets of behaviour means this:
BlockingDeque Extends BlockingQueue
The BlockingDeque
interface extends the BlockingQueue
interface. That means that you can use aBlockingDeque
as a BlockingQueue
. If you do so, the various inserting methods will add the elements to the end of the deque, and the removing methods will remove the elements from the beginning of the deque. The inserting and removing methods of the BlockingQueue
interface, that is.
Here is a table of what the methods of the BlockingQueue
does in a BlockingDeque
implementation:
BlockingQueue | BlockingDeque |
add() | addLast() |
offer() x 2 | offerLast() x 2 |
put() | putLast() |
remove() | removeFirst() |
poll() x 2 | pollFirst() |
take() | takeFirst() |
element() | getFirst() |
peek() | peekFirst() |
BlockingDeque Implementations
Since BlockingDeque
is an interface, you need to use one of its many implementations to use it. The java.util.concurrent
package has the following implementations of the BlockingDeque
interface:
The LinkedBlockingDeque
is a Deque
which will block if a thread attempts to take elements out of it while it is empty, regardless of what end the thread is attempting to take elements from.
Here is how to instantiate and use a LinkedBlockingDeque
:
BlockingDeque<String> deque = new LinkedBlockingDeque<String>(); deque.addFirst("1"); deque.addLast("2"); String two = deque.takeLast(); String one = deque.takeFirst();
BlockingDeque Code Example
Here is a small code example of how to use the BlockingDeque
methods:
BlockingDeque<String> deque = new LinkedBlockingDeque<String>(); deque.addFirst("1"); deque.addLast("2"); String two = deque.takeLast(); String one = deque.takeFirst();