queue
Queue is a special linear table. The special feature is that it only allows deletion at the front of the table and insertion at the back of the table. Like stack, queue is a linear table with restricted operation. The end of insertion is called tail end and the end of deletion is called queue head
The action of inserting a queue element into the queue is called queue in, and deleting a queue element from the queue is called queue out. Because the queue can only be inserted at one end and deleted at the other end, only the earliest element entering the queue can be deleted from the queue first. Therefore, the queue is also called FIFO – first in first out linear table
Blocking queue
- Support blocking insertion method: it means that when the queue is full, the queue will block the thread inserting elements and know that the queue is not full
- Supports the blocking removal method, which means that when the queue is empty, the thread getting the element will wait for the queue to become non empty
using producer and consumer mode in concurrent programming can solve most concurrent problems. This mode can improve the overall data processing speed of the program by balancing the working capacity of production threads and consumer threads
in the thread, the producer is the thread of production data, and the consumer is the thread of consumption data. In multi-threaded development, if the processing speed of the producer is greater than that of the consumer, the producer must wait for the consumer to process the data before continuing to produce data. If the processing capacity of the consumer is greater than that of the producer, the consumer must wait for the producer
In order to solve the problem of uneven production and consumption capacity, the producer and consumer model is proposed. The producer and consumer model solves the "strong coupling" problem between producers and consumers through a container. Producers and consumers do not communicate directly with each other, but communicate through the blocking queue. Therefore, after producing data, producers do not need to wait for consumers to process, but directly throw it to the blocking queue. Consumers do not ask producers for data, but directly get data from the blocking queue. The blocking queue is equivalent to a buffer, It balances the processing capacity of producers and consumers
blocking queue is often used in the scenario of producers and consumers. Producers are the threads that add elements to the queue, consumers are the threads that take elements out of the queue, and blocking queue is the container used by producers to store elements and consumers to obtain elements
Processing methods \ methods | Insertion method | removal method | inspection method |
---|---|---|---|
Throw exception | add(e) | remove() | element() |
Return special value | offer(e) | poll() | peek() |
Continuous blocking | put(e) | take() | nothing |
Timeout exit | offer(e, time, unit) | poll(time, unit) | nothing |
- Throw exception: when the queue is full, if an element is inserted into the queue, an IllegalStateException ("Queuefull") exception will be thrown. When the queue is empty, the element obtained from the queue will throw a NoSuchElementException exception
- Return special value: when inserting an element into the queue, it will return whether the element was successfully inserted. If the method is removed, it will take an element from the queue. If not, it will return null
- Continuous blocking: when the blocking queue is full, if the producer thread put s elements into the queue, the queue will block the production thread until the queue is available or exits in response to an interrupt. When the queue is empty, if the consumer thread take s elements from the queue, the queue will block the consumer thread until the queue is not empty
- Timeout exit: when the blocking queue is full, if the producer thread inserts elements into the queue, the queue will block the producer for a period of time. If the specified time is exceeded, the producer thread will exit
/***Class description: the function of fetching expired orders*/ public class FetchOrder implements Runnable { private DelayQueue<ItemVo<Order>> queue; public FetchOrder(DelayQueue<ItemVo<Order>> queue){ this.queue = queue;
Common blocking queue
- ArrayBlockingQueue: a bounded blocking queue consisting of an array structure
- Linked blocking queue: a bounded blocking queue composed of linked list structure
- PriorityBlockingQueue: an unbounded blocking queue that supports prioritization
- DelayQueue: an unbounded blocking queue implemented using priority queue
- Synchronous queue: a blocking queue that does not store elements
- LinkedTransferQueue: an unbounded blocking queue composed of a linked list structure
- Linked blocking deque: a bidirectional blocking queue composed of linked list structure
The above blocking queues all implement the BlockingQueue interface and are thread safe
Bounded blocking & & unbounded blocking
finite queue means that the length is limited. When it is full, the producer will enter the blocking state. Unbounded queue means that unlimited things can be placed in it without being blocked due to the queue length limit. Of course, the space limit comes from the limitation of system resources. If it is not processed in time, the queue will become larger and larger, exceeding a certain limit, resulting in memory overrun, The operating system or JVM will kill the OOM directly
it should be noted that unbounded will also cause blocking, because blocking is not only reflected in the blocking when producers put in elements, but also when consumers put in elements, if there are no elements, they will also block
ArrayBlockingQueue
ArrayBlockingQueue is a bounded blocking queue implemented by array. This queue sorts the elements according to the first in first out (FIFO) principle. By default, it is not guaranteed that threads can access the queue fairly. The so-called fair access queue is that threads blocked by value can access the queue according to the blocking order, that is, threads blocked first can access the queue first, Unfairness is unfair to the threads waiting first. When the queue is available, blocked threads can compete for the qualification to access the queue. It is possible to block the thread first and then access the queue. Parameters can be set during initialization
LinkedBlockingQueue
LinkedBlockingQueue is a bounded blocking queue implemented by linked list. The default and maximum length of this queue is Inreger.MAX_VALUE, this queue sorts the elements according to the first in first out principle
The difference between Array implementation and Linked implementation
- The implementation of locks in queues is different
the locks in the queue implemented by ArrayBlockingQueue are not separated, that is, production and consumption use the same lock
the locks in the queue implemented by LinkedBlockingQueue are separated, that is, putLock is used for production and TakeLock is used for consumption
- Different operations in production or consumption
in the queue implemented by ArrayBlockingQueue, enumeration objects are directly inserted or removed during production and consumption
in the queue implemented by LinkedBlockingQueue, during production and consumption, the enumeration object needs to be converted into a Node for insertion or removal, which will affect the performance
- The queue size is initialized differently
the size of the queue must be specified in the queue implemented by ArrayBlockingQueue
the size of the queue can not be specified in the queue implemented by LinkedBlockingQueue, but the default is Integer.MAX_VALUE
PriorityBlockingQueue
PriorityBlockingQueue is an unbounded blocking queue that supports priority. By default, elements are arranged in natural ascending order. You can also customize the class to implement the compareTo() method (Comparator) to specify the element sorting rule, or specify the construction parameter Comparator to sort the elements when initializing PriorityBlockingQueue. It should be noted that the order of elements with the same priority cannot be guaranteed.
DelayQueue
DelayQueue is an unbounded blocking queue that supports Delayed acquisition of elements. The queue is implemented by PriorityQueue. The elements in the queue must implement the Delayed interface. When creating elements, you can specify how long to obtain the current elements from the queue. Elements can be extracted from the queue only when the delay period expires
Application scenario:
Design of stand-alone cache system: you can use DelayQueue to save the validity period of cache elements, and use a thread to query DelayQueue circularly. Once the element can be obtained from the DelayQueue blocking queue, it indicates that the validity period of the element has expired
Experiment code:
Class description: the function of fetching expired orders:
/** *Class description: the function of fetching expired orders */ public class FetchOrder implements Runnable { private DelayQueue<ItemVo<Order>> queue; public FetchOrder(DelayQueue<ItemVo<Order>> queue){ this.queue = queue; } @Override public void run() { while(true) { try { ItemVo<Order> item = queue.take(); Order order = (Order)item.getData(); System.out.println("Get From Queue:"+"data=" +order.getOrderNo()+";"+order.getOrderMoney()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Class description: stored queue elements:
import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; /** *Class description: the element of the stored queue, */ public class ItemVo<T> implements Delayed { //Expiration time, but the value passed in represents the expiration time, in milliseconds private long activeTime; private T data;//Business data, generic //Incoming expiration duration, in seconds, internal conversion public ItemVo(long expirationTime, T data) { this.activeTime = expirationTime*1000+System.currentTimeMillis(); this.data = data; } public long getActiveTime() { return activeTime; } public T getData() { return data; } /* * This method returns the remaining time of the activation date. The time unit is specified by the unit parameter. */ public long getDelay(TimeUnit unit) { long d = unit.convert(this.activeTime -System.currentTimeMillis(),unit); return d; } /* *Delayed The interface inherits the Comparable interface and is sorted according to the remaining time. The actual calculation accuracy is nanoseconds */ public int compareTo(Delayed o) { long d = (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS)); if (d==0){ return 0; }else{ if (d<0){ return -1; }else{ return 1; } } } }
Class description: entity class of the order:
/** *Class description: entity class of the order */ public class Order { private final String orderNo;//Order number private final double orderMoney;//Amount of the order public Order(String orderNo, double orderMoney) { super(); this.orderNo = orderNo; this.orderMoney = orderMoney; } public String getOrderNo() { return orderNo; } public double getOrderMoney() { return orderMoney; } }
Class description: push order into queue:
import java.util.concurrent.DelayQueue; /** *Class description: push order into queue */ public class PutOrder implements Runnable { private DelayQueue<ItemVo<Order>> queue; public PutOrder(DelayQueue<ItemVo<Order>> queue){ this.queue = queue; } @Override public void run() { //Expires in 5 seconds Order orderTb = new Order("Tb12345",366); ItemVo<Order> itemTb = new ItemVo<Order>(5,orderTb); queue.offer(itemTb); System.out.println("Timeout after 5 seconds of order:"+orderTb.getOrderNo()+";" +orderTb.getOrderMoney()); //Expires in 8 seconds Order orderJd = new Order("Jd54321",366); ItemVo<Order> itemJd = new ItemVo<Order>(8,orderJd); queue.offer(itemJd); System.out.println("Timeout after 8 seconds of order:"+orderJd.getOrderNo()+";" +orderJd.getOrderMoney()); } }
Class description: delay queue test procedure:
import java.util.concurrent.DelayQueue; /** *Class description: delay queue test program */ public class Test { public static void main(String[] args) throws InterruptedException { DelayQueue<ItemVo<Order>> queue = new DelayQueue<ItemVo<Order>>();//Delay queue new Thread(new PutOrder(queue)).start(); new Thread(new FetchOrder(queue)).start(); //Print a number every 500 milliseconds for(int i=1;i<15;i++){ Thread.sleep(500); System.out.println(i*500); } } }
SynchronousQueue
synchronous queue is a blocking queue that does not store elements. Each put operation must wait for a take operation (put and take are methods for continuous output), otherwise elements cannot be added. The synchronous queue can be regarded as a passer who is responsible for directly passing the data processed by the producer thread to the consumer thread. The queue itself does not store any elements, which is very suitable for transitivity scenarios. The throughput of synchronous queue is higher than LinkedBlockingQueue and ArrayBlockingQueue
LinkedTransferQueue
Linkedtransgerqueue main methods:
transfer()
if a consumer is currently waiting to receive an element (when the consumer uses the take() method or the poll() method with time limit), transfer() can immediately transmit the element passed in by the producer to the consumer. If no consumer is waiting to receive an element, transfer() will store the element in the tail node of the queue and return it after the element is consumed by the consumer
tryTransfer()
tryTransfer() is used to test whether the producer or the incoming element can be directly passed to the consumer. If there is no consumer waiting to receive the element, it returns false. The difference between tryTransfer() and transfer() is that the method returns immediately regardless of whether the consumer accepts it or not, while transfer() returns only after the consumer has consumed it
LinkedBlockingDeque
LinkedBlockingDeque is a bidirectional blocking queue composed of linked list structure. The so-called two-way queue means that elements can be inserted and removed from both ends of the queue. Because there is one more entry to the two-way queue, when multiple threads join the queue at the same time, the competition is reduced by half.
addFirst, addLast, offerFirst, offerLast, peekFirst, peekLast and other methods are added. The method ending with the word First represents inserting, obtaining (peek) or removing the First element of the double ended queue. A method ending with the Last word that inserts, gets, or removes the Last element of a double ended queue. In addition, the insert method add is equivalent to addLast, and the remove method remove is equivalent to removeFirst. However, the take method is equivalent to takeFirst. I don't know if it's a JDK bug. It's clearer to use the method with First and Last suffixes. When initializing LinkedBlockingDeque, you can set the capacity to prevent it from over expanding. In addition, bidirectional blocking queue can be used in "work stealing" mode