Concurrent Containers and Frameworks - Concurrent Containers (2)

1. What is a blocking queue

    A BlockingQueue is a queue that supports two additional operations. These two additional operations support blocking insert and remove methods.

  1. Insert methods that support blocking: means that when the queue is full (except for unbounded queues), the queue blocks the thread that inserts elements until the queue is full.
  2. Blocking removal methods are supported: This means that when the queue is empty, the thread that gets the element will wait for the queue to become non-empty.

    Blocking queues are often used in producer and consumer scenarios. The producer is the thread that adds elements to the queue, and the consumer is the thread that takes elements from the queue. A blocking queue is a container that producers use to store elements and consumers use to get elements. The blocking queue provides 4 ways to handle two additional operations, insert and remove.

  1. Throwing an 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, getting an element from the queue will throw a NoSuchElementException.
  2. Return special value: When inserting an element into the queue, it will return whether the element was inserted successfully, and return true if successful. If it is a remove method, it will take an element from the queue, otherwise it will return null.
  3. Blocking all the time: When the blocking queue is full, if the producer thread puts elements into the queue, the queue will block the producer thread until the queue is available or exits in response to an interrupt. When the queue is empty, if the consumer thread takes elements from the queue, the queue will block the consumer thread until the queue is not empty.
  4. Timeout exit: When the blocking queue is full, if the producer thread inserts elements into the queue, the queue will block the producer thread for a period of time. If the specified time is exceeded, the producer thread will exit.

    Note : If it is an unbounded blocking queue, the queue cannot be full, so the put or offer method will never be blocked, and when the offer method is used, the method will always return true.

2. Blocking queue

    2.1 Classification

  1. ArrayBlockingQueue: A bounded blocking queue consisting of array structures.
  2. LinkedBlockingQueue: A bounded blocking queue consisting of a linked list structure.
  3. PriorityBlockingQueue: An unbounded blocking queue that supports priority sorting.
  4. DelayQueue: An unbounded blocking queue implemented using a priority queue.
  5. SynchronousQueue: A blocking queue that does not store elements.
  6. LinkedTransferQueue: An unbounded blocking queue consisting of a linked list structure.
  7. LinkedBlockingDeque: A bidirectional blocking queue consisting of a linked list structure.

    2.2 Detailed explanation of the use of seven blocking queues

  1. ArrayBlockingQueue: is a bounded blocking queue implemented with an array. This queue sorts elements on a first-in, first-out basis. By default, fair access queues are not guaranteed for threads. The so-called fair access queues refer to blocked threads that can access the queues in the order of blocking, that is, the blocked threads first access the queues. Unfairness is unfair to the thread that waits first. When the queue is available, the blocked thread can compete for the qualification to access the queue, and it is possible that the blocked thread will access the queue last. To ensure fairness, throughput is usually reduced. Visitor fairness is achieved using reentrant locks.
    public class TestBlockingQueue {
    	public static void main(String[] args) {
    		int capacity=1000;//有界队列元素容量,必须>=1
    		ArrayBlockingQueue<String> arr1=new ArrayBlockingQueue<String>(capacity);
    		ArrayBlockingQueue<String> arr2=new ArrayBlockingQueue<String>(capacity,true);//boolean值表示是否采用公平性原则
    		
    	}
    }

     

  2. LinkedBlockingQueue: LinkedBlockingQueue is a bounded blocking queue implemented with a linked list. The default and maximum length of this queue is Integer.MAX_VALUE. This queue sorts elements on a first-in, first-out basis.
  3. PriorityBlockingQueue: is an unbounded blocking queue that supports priority. By default, elements are sorted in ascending natural order. You can also customize the class to implement the compareTo() method to specify the element sorting rules, or when initializing the PriorityBlockingQueue, specify the construction parameter Comparator to sort the elements. Note that the order of elements of the same priority is not guaranteed.
  4. DelayQueue: DelayQueue is an unbounded blocking queue that supports delayed acquisition of elements. Queues are implemented using PriorityQueue. The elements in the queue must implement the Delayed interface (refer to the implementation of the ScheduledFutureTask class in ScheduledThreadPoolExecutor ) . When creating an element, you can specify how long it takes to get the current element from the queue. Elements can be drawn from the queue only when the delay expires. This blocking queue is very useful. For example, a cache system can be designed, and a thread can be used to cyclically query the DelayQueue. Once the elements can be obtained from the DelayQueue, it means that the cache validity period has expired; it can also be used for timed task scheduling, using the DelayQueue to save the tasks that will be executed on the day and Execution time, once the task is obtained from DelayQueue, it starts to execute. For example, TimerQueue is implemented using DelayQueue.
  5. SynchronousQueue: SynchronousQueue is a blocking queue that does not store elements. Each put operation must wait for a take operation, otherwise it cannot continue to add elements. It supports fair access queues. By default, threads access queues using an unfair policy. Use the following constructor to create a SynchronousQueue with fair access. If set to true, waiting threads will access the queue in a first-in, first-out order. The queue can be understood as a passer responsible for passing the data processed by the producer thread directly to the consumer thread. The queue itself does not store any elements and is ideal for transitive scenarios.
  6. LinkedTransferQueue: LinkedTransferQueue is an unbounded blocking TransferQueue queue composed of a linked list structure. Compared with other blocking queues, LinkedTransferQueue has more tryTransfer and transfer methods.
    1. Use the transfer method: If there are consumers currently waiting to receive elements (when the consumer uses the take() method or the poll() method with a time limit), the transfer method can transfer the elements passed in by the producer to the consumer immediately By. If there is no consumer waiting to receive the element, the transfer method will store the element in the tail node of the queue and wait until the element is consumed by the consumer before returning.
    2. tryTransfer method: The tryTransfer method is used to test whether the elements passed in by the producer can be directly passed to the consumer. Returns false if no consumers are waiting to receive the element. The difference between the tryTransfer method and the transfer method is that the method returns immediately regardless of whether the consumer receives it or not, while the transfer method must wait for the consumer to consume before returning. For the tryTransfer(E e, long timeout, TimeUnit unit) method with a time limit, try to pass the element passed in by the producer to the consumer directly, but if no consumer consumes the element, it will wait for the specified time before returning, if If the element has not been consumed within the timeout period, it will return false, and if the element has been consumed within the timeout period, it will return true.
  7. LinkedBlockingDeque: LinkedBlockingDeque is a bidirectional blocking queue consisting of a linked list structure . The two-way queue has an additional entry to the operation queue, which reduces the competition by half when multiple threads join the queue at the same time. Compared with other blocking queues, LinkedBlockingDeque has more methods such as addFirst, addLast, offerFirst, offerLast, peekFirst, and peekLast. The methods ending with the word First indicate inserting, getting (peek) or removing the first element of the double-ended queue. A method ending with the word Last to insert, get, or remove the last element of the deque. In addition, the insertion method add is equivalent to addLast, and the removal method remove is equivalent to removeFirst. But the take method is equivalent to takeFirst

3. Implementation principle of blocking queue

    The JDK implements this through the notification pattern. The so-called notification mode means that when the producer adds elements to the full queue, the producer will be blocked, and when the consumer consumes an element in the queue, the producer will be notified that the current queue is available. By looking at the JDK source code, it is found that ArrayBlockingQueue is implemented using Condition. That is, through await/signal to achieve.

 final Object[] items;
 final ReentrantLock lock;
 private final Condition notEmpty;
 private final Condition notFull;
 int count;
 public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
 }
 public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
 private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }
 private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325478224&siteId=291194637