concurrent包阻塞队列详解

     在编程中我们经常会使用到Queue容器类,但是这些容器类不是线程安全的,因此concurrent包中Doug Lea大师
为我们准备了对应的线程安全的容器类;每一种容器类也满足了对应的使用场景;那些本文就是梳理这些并发的Queue容
器类及使用场景;

 

1、主要容器实现类

         ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, SynchronousQueue

 

2、常用的BlockingQueue基本操作


插入操作:BlockingQueue实现的3和4

  1. add(E e) :往队列插入数据,当队列满时,插入元素时会抛出IllegalStateException异常;
  2. offer(E e):当往队列插入数据时,插入成功返回true,否则则返回false。当队列满时不会抛出异常;
  3. put:当阻塞队列容量已经满时,往阻塞队列插入数据的线程会被阻塞,直至阻塞队列已经有空余的容量可供使用;
  4. offer(E e, long timeout, TimeUnit unit):若阻塞队列已经满时,同样会阻塞插入数据的线程,直至阻塞队列已经有空余的地方,与put方法不同的是,该方法会有一个超时时间,若超过当前给定的超时时间,插入数据的线程会退出;

删除操作BlockingQueue实现的3和4

  1. remove(Object o):从队列中删除数据,成功则返回true,否则为false
  2. poll:删除数据,当队列为空时,返回null;
  3. take():当阻塞队列为空时,获取队头数据的线程会被阻塞;
  4. poll(long timeout, TimeUnit unit):当阻塞队列为空时,获取数据的线程会被阻塞,另外,如果被阻塞的线程超过了给定的时长,该线程会退出

查看操作

  1. element:获取队头元素,如果队列为空时则抛出NoSuchElementException异常;
  2. peek:获取队头元素,如果队列为空则抛出NoSuchElementException异常

 

3、常用BlockingQueue详解

   ①ArrayBlockingQueue 是由数组实现的有界阻塞队列,队列命令元素FIFO(先进先出);当队列容量满时放入元素操作会阻塞,当队列为null时获取一个元素也会阻塞;

   默认情况下这种队列不能保证访问队列的公平性,这是为了提升吞吐量;确保公平性可以通过在构造函数中增加true设定;

 

        ②LinkedBlockingQueue 是用链表实现的有界阻塞队列,也是FIFO特性;具有更高的吞吐量;  为了防止迅速增加耗损大量的内容,通常创建对象时会指定其初始大小,未指定则为Interger.MAX_VALUE

 

        ③PriorityBlockingQueue 是一个支持优先级的无界阻塞队列,元素采用自然顺序进行排序,可以通过自定义类实现compareTo()方法来指定元素排序规则,或者初始化时通过构造器参数Comparator指定排序规则

 

        ④SynchrousQueue 不存储任何元素的阻塞队列,只有当其他线程删除数据才能插入数据,可以通过构造器参数来指定公平性;

 

   ⑤LinkedTransferQueue 由链表数据结构构成的无界阻塞队列,实现了TransferQueue接口,相比其他阻塞队列有以下不同方法:

transfer(E e)
如果当前有线程(消费者)正在调用take()方法或者可延时的poll()方法进行消费数据时,生产者线程可以调用transfer方法将数据传递给消费者线程。如果当前没有消费者线程的话,生产者线程就会将数据插入到队尾,直到有消费者能够进行消费才能退出;

tryTransfer(E e)
tryTransfer方法如果当前有消费者线程(调用take方法或者具有超时特性的poll方法)正在消费数据的话,该方法可以将数据立即传送给消费者线程,如果当前没有消费者线程消费数据的话,就立即返回false。因此,与transfer方法相比,transfer方法是必须等到有消费者线程消费数据时,生产者线程才能够返回。而tryTransfer方法能够立即返回结果退出。

tryTransfer(E e,long timeout,imeUnit unit)</br>
与transfer基本功能一样,只是增加了超时特性,如果数据才规定的超时时间内没有消费者进行消费的话,就返回false

        ⑥LinkedBlockingDeque 基于链表数据结构的有界阻塞双端队列,在创建对象时为指定大小时,其默认大小为Integer.MAX_VALUE,

        ⑦DelayQueue 是一个存放实现Delayed接口的数据的无界阻塞队列,只有当数据对象的延时时间达到时才能插入到队列进行存储;  当前多有的数据都还没有达到创建时所指定的延时期,则队列没有队头,并且线程通过poll等方法获取数据元素则返回null。所谓数据延时期满时,则是通过Delayed接口的getDelay(TimeUnit.NANOSECONDS)来进行判定,如果该方法返回的是小于等于0则说明该数据元素的延时期已满。

 

 

4,ArrayBlockingQueue源码解析

  使用案例:通过ArrayBlockingQueue实现消费者-生产者模式

package com.hezm.thread.day1.collections;

import java.util.concurrent.ArrayBlockingQueue;

public class BlockingDemo {

    /***
    * 利用 ArrayBlockingQueue 的添加已满会阻塞和获取已空会阻塞实现生产者-消费者模式
    *
    */
    ArrayBlockingQueue<String> ab = new ArrayBlockingQueue(10);

    public void init() {
        new Thread(()->{
            try {
                String data = ab.take(); //阻塞方式获取数据;
                System.out.println("receive:" + data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public void addData(String data) {
        ab.add(data);
        try {
            System.out.println("send:" + data);
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        BlockingDemo blockingDemo = new BlockingDemo();
        for (int i = 0; i < 1000; i++) {
            blockingDemo.addData("data:" + i);
            blockingDemo.init();
        }
    }
}

     运行结果:

send:data:0
send:data:1
receive:data:0
send:data:2
receive:data:1
send:data:3
receive:data:2
send:data:4
receive:data:3
send:data:5
receive:data:4
send:data:6
receive:data:5
send:data:7
receive:data:6

 

      源码分析:

/** 
* 关键元素: notEmpty 不为null唤醒 、  notFull 不为满唤醒
*     天然满足生产者-消费者:在为null的时候消费者阻塞等待,有值之后消费者被唤醒;
*        在队列满的时候生产者阻塞等待,不满的时候唤醒生产; 
*
*add{
* offer(){
* 重入锁{ enqueue(e) 插入队列
* if (++putIndex == items.length) putIndex = 0; //当要插入的数组下标满了则重置为0;
* notEmpty.signal(); //去唤醒 take中的 notEmpty.await()
* }
* }
* }
*
*
* take{
* 重入锁中获取可中断的锁;
* notEmpty.await() 阻塞等待队列中设置进值
* dequeue() 出队列;{
* if (++takeIndex == items.length) takeIndex = 0; 获取队列的数组下标;
* itrs.elementDequeued(); // 维护迭代器中的内容;
* notFull.signal();
* }
* }
* remove() 移除
*/

//add()   往队列添加元素
   public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;   //重入锁锁定资源
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);    //插入队列
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

    
     private void enqueue(E x) {   //入队列
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)  //putIndex 当插入下标已满则重置为0,循环使用;
            putIndex = 0;
        count++;
        notEmpty.signal();    //非null唤醒;
    }



//take()  阻塞等待队列数据进行消费
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();        //重入锁-可中断的锁获取
        try {
            while (count == 0)
                notEmpty.await();    //当队列数为0时非null等待
            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)   //出队已满则重置读取下标为0;
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();              //未满队列等待
        return x;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值