34 What Is a Blocking Queue

34 What Is a Blocking Queue #

In this lesson, we will mainly explain what a blocking queue is.

The Role of Blocking Queues #

A blocking queue, also known as BlockingQueue, is an interface. Here is the code:

public interface BlockingQueue<E> extends Queue<E>{...}

BlockingQueue inherits from the Queue interface and is a type of queue introduced in Java 5.

BlockingQueue is thread-safe, and we can use thread-safe queues to elegantly solve the thread safety problems in many scenarios. For example, when using the producer/consumer pattern, the producer only needs to add elements to the queue, and the consumer only needs to retrieve them from the queue. The diagram below illustrates this: img

In the diagram, there are three producer threads on the left side, which will put the produced results into the blocking queue in the middle. The three consumers on the right side will retrieve the content they need from the blocking queue and process it. Because the blocking queue is thread-safe, both the producer and consumer can be multi-threaded without any thread safety issues.

Since the queue itself is thread-safe, it can safely pass data from one thread to another. Therefore, our producer/consumer can directly use a thread-safe queue without having to consider more thread safety issues. This means that the responsibility of considering thread safety, such as locks, has been transferred from “you” to the “queue”, reducing the difficulty and workload of our development.

At the same time, a queue can also serve as an isolation mechanism. For example, if we develop a bank transfer program, the producer thread does not need to be concerned with the specific transfer logic. It only needs to put the transfer task, such as account and amount information, into the queue, without concerning itself with how the bank class implements the specific transfer business. As for the bank class, it will retrieve the specific task to be executed from the queue, and then use its own methods to complete the transfer.

This decouples the specific task from the class that executes it. The task is placed in the blocking queue, and the thread responsible for putting tasks into the queue cannot directly access the object that implements the specific transfer operation of our bank, achieving isolation and improving security.

Main Concurrent Queue Relationships #

img

The above diagram shows the main implementation classes of Queue. It can be seen that the thread-safe queues (also known as concurrent queues) provided by Java are divided into two categories: blocking queues and non-blocking queues.

A typical example of a blocking queue is the implementation class of the BlockingQueue interface. Below it, there are 6 main implementations of BlockingQueue, which are ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue, DelayQueue, PriorityBlockingQueue, and LinkedTransferQueue. Each has its own characteristics. We will explain the characteristics of these commonly used blocking queues in Lesson 36.

A typical example of a non-blocking concurrent queue is ConcurrentLinkedQueue, which does not block threads and ensures thread safety through CAS (Compare And Swap).

We can freely choose to use either a blocking queue or a non-blocking queue according to our business requirements.

There is also a closely related interface called Deque, which inherits from Queue. Here is the code:

public interface Deque<E> extends Queue<E> {//...}

Deque stands for double-ended queue, and it can add and remove elements from both ends. In contrast, a regular queue can only enter from one end and exit from the other end. This is the difference between Deque and Queue. Other aspects of Deque are similar to Queue.

Characteristics of Blocking Queues #

The most important characteristic of a blocking queue, which distinguishes it from other types of queues, is its “blocking” property. Below, we will focus on the blocking function, which balances the capabilities of the producer and consumer. When one end operates too quickly, the blocking queue slows down the excessive speed. The most important two methods for implementing blocking are the take and put methods.

take method #

The take method is used to retrieve and remove the head node of the queue. It can be normally removed when the queue contains data. However, if the take method is executed and the queue is empty, it blocks until the queue has data. Once there is data in the queue, it immediately unblocks and retrieves the data. The process is shown in the following diagram:

img

put method #

When the put method inserts an element, if the queue is not full, it can be inserted normally just like a regular insertion. However, if the queue is full, it cannot continue to insert and blocks until there is available space in the queue. If there is available space later, such as when a consumer consumes an element, the queue unblocks and adds the data that needs to be inserted to the queue. The process is shown in the following diagram:

img

The blocking and unblocking actions in the above processes are all completed by BlockingQueue. We don’t need to handle them ourselves.

Boundedness (Capacity) #

In addition, a blocking queue has a very important attribute, which is the size of its capacity. It can be either bounded or unbounded.

An unbounded queue means that it can hold a very large number of elements. For example, the upper limit of LinkedBlockingQueue is Integer.MAX_VALUE, which is approximately 2 to the power of 31, a very large number. We can consider it to have unlimited capacity because it is almost impossible to fill this capacity.

However, some blocking queues are bounded. For example, if the capacity of ArrayBlockingQueue is full, it will not expand. Once it is full, no more data can be placed inside.

These are all the contents of this lesson. In this lesson, we explained what a blocking queue is. First, we explained the role of a blocking queue. Then we looked at the concurrent queues in Java 8, which are divided into blocking queues and non-blocking queues, with 6 main implementations of blocking queues. Finally, we examined the characteristics of blocking queues, including the take and put methods, and whether they are bounded.