java multithreading blocking queue

java multithreading blocking queue

 

       A BlockingQueue is a queue that supports two additional operations. The two additional operations are: when the queue is empty, the thread that gets the element waits for the queue to become non-empty. When the queue is full, the thread storing the element waits for the queue to become available. 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 for producers to store elements, and consumers only get elements from the container.

 

Blocking queues provide four processing methods:

 



 

  • Throwing an exception : When the blocking queue is full, inserting elements into the queue will throw an IllegalStateException ("Queue full") exception. When the queue is empty, a NoSuchElementException is thrown when an element is retrieved from the queue.
  • Returns a special value : the insert method will return whether it succeeded, and true if it succeeded. The remove method is to take an element from the queue, and return null if there is no
  • 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 it gets the data or exits in response to the interrupt. When the queue is empty, the consumer thread tries to take elements from the queue, and the queue also blocks the consumer thread until the queue is available.
  • Timeout exit : When the blocking queue is full, the queue will block the producer thread for a period of time. If it exceeds a certain time, the producer thread will exit.

 

 

Blocking queue implementation class

 

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

 

ArrayBlockingQueue

 

       ArrayBlockingQueue is a bounded blocking queue implemented with arrays. This queue sorts elements on a first-in-first-out (FIFO) basis. By default, fair access to the queue is not guaranteed for visitors. The so-called fair access queue refers to all blocked producer threads or consumer threads. When the queue is available, the queue can be accessed in the order of blocking, that is, the first blocked producer thread. , you can insert elements into the queue first, and the consumer thread that blocks first can get elements from the queue first. Usually throughput is reduced to ensure fairness.

 

 

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.

 

 

PriorityBlockingQueue

 

       PriorityBlockingQueue is an unbounded queue that supports priorities. By default, elements are arranged in natural order, and the collation of elements can also be specified through the comparator. Elements are sorted in ascending order.

 

 

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, and you can specify how long to get the current element from the queue when you create the element. Elements can be drawn from the queue only when the delay expires.

 

We can use DelayQueue in the following application scenarios:

 

  • Design of the cache system: DelayQueue can be used to save the validity period of cache elements, and a thread can be used to query DelayQueue cyclically. Once elements can be obtained from DelayQueue, it means that the cache validity period has expired.
  • Timed task scheduling. Use DelayQueue to save the tasks and execution time that will be executed on the day. Once the tasks are obtained from DelayQueue, they will be executed. For example, TimerQueue is implemented using DelayQueue.

 

SynchronousQueue

 

       SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue 和 ArrayBlockingQueue。

 

 

LinkedTransferQueue

 

       LinkedTransferQueue是一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列LinkedTransferQueue多了tryTransfer和transfer方法。

 

 

LinkedBlockingDeque

 

       LinkedBlockingDeque是一个由链表结构组成的双向阻塞队列。所谓双向队列指的你可以从队列的两端插入和移出元素。双端队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。相比其他的阻塞队列,LinkedBlockingDeque多了addFirst,addLast,offerFirst,offerLast,peekFirst,peekLast等方法,以First单词结尾的方法,表示插入,获取(peek)或移除双端队列的第一个元素。以Last单词结尾的方法,表示插入,获取或移除双端队列的最后一个元素。另外插入方法add等同于addLast,移除方法remove等效于removeFirst。但是take方法却等同于takeFirst,不知道是不是Jdk的bug,使用时还是用带有First和Last后缀的方法更清楚。在初始化LinkedBlockingDeque时可以初始化队列的容量,用来防止其再扩容时过渡膨胀。另外双向阻塞队列可以运用在“工作窃取”模式中。

 

 

 

 

阻塞队列应用场景

 

       阻塞队列的最常使用的例子就是生产者消费者模式,也是各种实现生产者消费者模式方式中首选的方式。使用者不用关心什么阻塞生产,什么时候阻塞消费,使用非常方便,代码如下:

 

 

package MyThread;

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class BlockingQueueTest {
    //生产者
    public static class Producer implements Runnable{
        private final BlockingQueue<Integer> blockingQueue;
        private volatile boolean flag;
        private Random random;

        public Producer(BlockingQueue<Integer> blockingQueue) {
            this.blockingQueue = blockingQueue;
            flag=false;
            random=new Random();

        }
        public void run() {
            while(!flag){
                int info=random.nextInt(100);
                try {
                    blockingQueue.put(info);
                    System.out.println(Thread.currentThread().getName()+" produce "+info);
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }               
            }
        }
        public void shutDown(){
            flag=true;
        }
    }
    //消费者
    public static class Consumer implements Runnable{
        private final BlockingQueue<Integer> blockingQueue;
        private volatile boolean flag;
        public Consumer(BlockingQueue<Integer> blockingQueue) {
            this.blockingQueue = blockingQueue;
        }
        public void run() {
            while(!flag){
                int info;
                try {
                    info = blockingQueue.take();
                    System.out.println(Thread.currentThread().getName()+" consumer "+info);
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }               
            }
        }
        public void shutDown(){
            flag=true;
        }
    }
    public static void main(String[] args){
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>(10);
        Producer producer=new Producer(blockingQueue);
        Consumer consumer=new Consumer(blockingQueue);
        //创建5个生产者,5个消费者
        for(int i=0;i<10;i++){
            if(i<5){
                new Thread(producer,"producer"+i).start();
            }else{
                new Thread(consumer,"consumer"+(i-5)).start();
            }
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        producer.shutDown();
        consumer.shutDown();

    }
}

 

 

 

 

 

阻塞队列实现原理

 

       阻塞队列实际上使用的就是是lock锁的多条件(condition)阻塞控制。使用BlockingQueue封装了根据条件阻塞线程的过程,而我们就不用关心繁琐的await/signal操作了。

 

Jdk 1.7中ArrayBlockingQueue部分代码:

 

 

public ArrayBlockingQueue(int capacity, boolean fair) {

        if (capacity <= 0)
            throw new IllegalArgumentException();
        //创建数组    
        this.items = new Object[capacity];
        //创建锁和阻塞条件
        lock = new ReentrantLock(fair);   
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
//添加元素的方法
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();
        }
    }
 //入队的方法
 private void enqueue(E x) {
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }
 //移除元素的方法
 public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
 //出队的方法
 private E dequeue() {
        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;

 

 

 

 

 

双端阻塞队列(BlockingDeque)

 

concurrent包下还提供双端阻塞队列(BlockingDeque),和BlockingQueue是类似的,只不过BlockingDeque提供从任意一端插入或者抽取元素的队列。

Guess you like

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