Java multi-thread (9) blocking queue, java multi-thread blocking queue
Reprinted please indicate the source: http://blog.csdn.net/xingjiarong/article/details/48005091
In the previous blog, we introduced how to use Object locks, conditional locks, and more convenient synchronized keywords to implement multi-thread synchronization and mutual exclusion. You may think it is very convenient to use the synchronized keyword, however, users must really understand the usage of synchronized and have some experience in multi-threaded programming. Otherwise, it is difficult to fully consider the problem and cause unexpected problems. In fact, there is a more convenient way to achieve synchronization and mutex than the synchronized keyword in java, and there is no need to consider complicated issues, that is, the use of blocking queues, this time we will discuss the use of blocking queues.
For example, we often give an example-bank transfer. Every time we write this program, we have to consider a lot of issues, such as how the two threads should be mutually exclusive, what should we do if the amount is insufficient. In fact, we can solve this problem elegantly and securely through a queue. The transfer thread inserts the transfer instruction object into a queue, instead of directly accessing the bank object. Another thread extracts commands from the queue to execute the transfer. Because only the thread can access the internal methods of the Bank object, synchronization and mutex are not required.
Blocking queue means that when an attempt is made to add an element to the queue and the queue is full, or the queue is empty to remove an element from the queue, the thread that executes this operation will be blocked. When coordinating multiple threads, blocking the queue is a useful tool. The worker thread can periodically store intermediate results in the queue, other threads remove intermediate results and perform further processing. The queue performs Automatic Load Balancing. When the queue is full, threads that add elements to the queue will be blocked. When the queue is empty, the thread that tries to retrieve elements from the queue is blocked.
Java mainly has the following blocking Queues:
1. ArrayBlockingQueue
This queue is implemented using a cyclic array. There are two constructor methods:
1) blocked Queues with a specified capacity
ArrayBlockingQueue(int capacity)
2) blocked Queues with specified capacity and fairness settings
ArrayBlockingQueue(int capacity,boolean fair)
The thread with the longest wait time will be given priority when a fair blocking queue is awakened, but this does not guarantee absolute fairness, because a fair blocking queue will reduce the efficiency, so it is generally not used, by default, the queue is blocked fairly.
2. Define blockingqueue
This queue is implemented using a linked list. There are two constructor methods:
1) construct an uncapped blocking queue
LinkedBlockingQueue();
2) construct a blocking queue with an upper limit
LinkedBlockingQueue(int capacity);
3. Define blockingdeque
The construction method and implementation principle of this queue are both the same. The difference between the two is that the LinkedBlockingDeque is a two-way queue.
4. DelayQueue
Construct an unbounded blocking queue containing Delayed elements with limited blocking time. Only those elements with excessive latency can be removed from the queue.
Constructor:DelayQueue();
5. PriorityBlockingQueue
The queue is blocked by priority. The queue with a higher priority is removed from the queue first. There are three constructor methods for this queue to be implemented using heap:
PriorityBlockingQueue();PriorityBlockingQueue(int initialCapacity);PriorityBlockingQueue(int initialCapacity,Comparator<? super E> compaator);
InitialCapacity: initial capacity of the priority queue. The default value is 11.
Comparator is used to compare elements. If not specified, the Comparable interface must be implemented for the elements.
Common Methods for blocking queues
Method |
Normal action |
Actions in special circumstances |
Add |
Add an element |
If the queue is full, an IllegalStateException is thrown. |
Remove |
Remove and return the Header element |
If the queue is empty, a NoSuchElementException exception is thrown. |
Put |
Add an element |
Blocking if the queue is full |
Take |
Remove and return the Header element |
If the queue is empty, it is blocked. |
Peek |
Return the Header element of the queue. |
If the queue is empty, null is returned. |
Poll |
Removes and returns the Header element of the queue. |
If the queue is empty, null is returned. |
Element |
Return the Header element of the queue. |
If the queue is empty, a NoSuchElementException exception is thrown. |
Offer |
Add an element and return true |
If the queue is full, false is returned. |
The following describes how to use a blocking queue to implement producer consumer problems:
The producer and consumer problem is a typical thread synchronization problem. Producers and consumers share a buffer zone and can be seen as warehouses. Producers and production products are placed in warehouses, and consumers take products from warehouses for consumption, when the Warehouse is empty, the consumer is blocked. When the Warehouse is full, the producer is blocked.
Import java. util. concurrent. arrayBlockingQueue; public class ProductThread implements Runnable {private ArrayBlockingQueue <Integer> queue; public ProductThread (ArrayBlockingQueue <Integer> queue) {this. queue = queue;} public void run () {while (true) {try {int product = (int) (Math. random () * 10);/** when the queue is full, it will block */queue. put (product); System. out. println (Thread. currentThread () + "produced product --" + product + "remaining element:" + queue. size (); Thread. sleep (1000);} catch (Exception e) {e. printStackTrace ();}}}}
Import java. util. concurrent. arrayBlockingQueue; public class ConsumeThread implements Runnable {private ArrayBlockingQueue <Integer> queue; public ConsumeThread (ArrayBlockingQueue <Integer> queue) {this. queue = queue;} public void run () {while (true) {try {/** when the queue is empty, */int product = queue. take (); System. out. println (Thread. currentThread () + "consumed element --" + product + "available space:" + queue. size (); Thread. sleep (1000);} catch (Exception e) {e. printStackTrace ();}}}}
Import java. util. concurrent. arrayBlockingQueue; public class Main {public static void main (String [] args) {// The circular array with a size of 10 blocks the queue ArrayBlockingQueue <Integer> queue = new ArrayBlockingQueue <> (10 ); /** use five producer threads to produce products */for (int I = 0; I <5; I ++) {new Thread (new ProductThread (queue )). start ();}/** use two consumer threads to consume the product */for (int I = 0; I <2; I ++) {new Thread (new ConsumeThread (queue )). start ();}}}
Running result:
Thread [Thread-2, 5, main] produces the product-4 residual elements: 4
Thread [Thread-5, 5, main] consumes elements -- 1 space remaining: 3
Thread [Thread-1, 5, main] produces the product -- 1 residual element: 3
Thread [Thread-6, 5, main] consumes elements -- 1 space remaining: 3
Thread [Thread-0, 5, main] produces the product-0 residual elements: 3
Thread [Thread-4, 5, main] produces the product -- 1 residual element: 3
Thread [Thread-3, 5, main] produces the product-9 remaining elements: 4
Thread [Thread-0, 5, main] produces the product -- 2 remaining elements: 5
Thread [Thread-1, 5, main] produces the product-9 remaining elements: 5
Thread [Thread-4, 5, main] produces the product-6 remaining elements: 7
Thread [Thread-2, 5, main] produces the product -- 2 remaining elements: 7
Thread [Thread-3, 5, main] produces the product -- 1 residual element: 7
Thread [Thread-5, 5, main] consumes elements -- 4 available space: 6
Thread [Thread-6, 5, main] consumes the element -- 0 remaining space: 7
Thread [Thread-0, 5, main] produces the product-0 residual elements: 7
Thread [Thread-4, 5, main] produces the product-7 remaining elements: 8
Thread [Thread-3, 5, main] produces the product-5 remaining elements: 9
Thread [Thread-1, 5, main] produces the product-4 remaining elements: 10
Thread [Thread-5, 5, main] consumes elements -- 9 available space: 9
Thread [Thread-2, 5, main] produces the product -- 9 remaining elements: 10
Thread [Thread-6, 5, main] consumes elements -- 2 remaining space: 9
Thread [Thread-1, 5, main] produces the product-4 remaining elements: 10
Thread [Thread-0, 5, main] produces the product -- 3 remaining elements: 10
Thread [Thread-6, 5, main] consumes elements -- 2 remaining space: 9
Thread [Thread-4, 5, main] produces the product-5 remaining elements: 10
Thread [Thread-5, 5, main] consumes elements -- 9 remaining space: 10
Thread [Thread-3, 5, main] produces the product -- 9 remaining elements: 10
Thread [Thread-5, 5, main] consumes elements-6 remaining space: 10
Thread [Thread-6, 5, main] consumes elements -- 1 space remaining: 10
Thread [Thread-2, 5, main] produces the product -- 2 remaining elements: 10
Thread [Thread-0, 5, main] produces the product -- 1 residual element: 10
Thread [Thread-5, 5, main] consumes the element -- 0 remaining space: 10
Thread [Thread-4, 5, main] produces the product-4 remaining elements: 10
Thread [Thread-6, 5, main] consumes elements -- 7 remaining space: 10
Thread [Thread-5, 5, main] consumes elements-5 remaining space: 10
From the results, we can see that the blocking queue is empty at the beginning, and multiple production threads are produced at the same time. When there is a product, two consumers start to consume it because there are a large number of producers, so the queue is gradually filled up. When the queue is full, the producer thread is congested and can only wait for the consumer to consume a product before reproduction. Therefore, the result of consuming a product and producing a product is finally displayed.
Source code download: http://download.csdn.net/detail/xingjiarong/9052057
Copyright Disclaimer: This article is the original author article, reprint please indicate the source, view the original article, please visit: http://blog.csdn.net/xingjiarong