阻塞队列 BlockingQueue

BlockingQueue介绍

阻塞队列,并发安全(ReentrantLock),不能插入null

BlockingQueue接口结构

在这里插入图片描述
在这里插入图片描述
定义了阻塞队列的几种插入和获取元素等方法

操作抛异常特殊值阻塞超时
插入add(E e)offer(E e)put(E e)offer(E e, long timeout, TimeUnit unit)
移除remove()poll()take()poll(long timeout, TimeUnit unit)
检查element()peek()--

子类实现基本也是这些方法,只是数据结构不一样.

实现类介绍

在这里插入图片描述

实现类介绍
ArrayBlockingQueue内部基于数组实现,一个put索引,一个task索引 ,内部锁(ReentrantLock)可设置公平与非公平
LinkedBlockingQueue内部链表实现,一个head,一个last,内部非公平锁(ReentrantLock),可指定大小,不指定Integer最大值
LinkedBlockingDeque双向队列,基于链表实现,内部一个ReentrantLock锁
PriorityBlockingQueue内部基于数组实现,默认大小11,自动扩容(Integer最大值-8 某些虚拟机头信息),内部非公平锁(ReentrantLock),compareTo()实现取数据顺序,同优先级顺序不确定
SynchronousQueue内部没有维护容器,使用队列必须绑定一个task,这样put才会成功,无锁采用cas
DelayQueue基于PriorityBlockingQueue实现的延迟队列(无界),元素需实现Delay接口,指定过期时间
LinkedTransferQueue比一般的阻塞队列多了tryTransfer和transfer方法预占位

ArrayBlockingQueue

基于数组实现,维护一个put索引和一个task索引, 先进先出. 可做有界缓冲区

    //3种构造方法
    @Test
    void constructorDemo(){
        //队列大小100,默认使用非公平锁
        ArrayBlockingQueue<Integer> arrayBlockingQueue1 = new ArrayBlockingQueue(100);

        //队列大小100,并且指定锁的是公平还是非给公平.   true公平 false非公平
        ArrayBlockingQueue<Integer> arrayBlockingQueue2 = new ArrayBlockingQueue(100,true);

        //大小100,内部使用非公平锁, 并且以collection初始化ArrayBlockingQueue
        Collection<Integer> collection = new ArrayList();
        collection.add(1);
        ArrayBlockingQueue<Integer> arrayBlockingQueue3 = new ArrayBlockingQueue(100,false,collection);
    }

LinkedBlockingQueue

基于链表实现的有界阻塞队列,一个head,一个last,内部非公平锁. 可定义大小,不定义的话Integer最大值,先进先出

    //3种构造方法
    @Test
    void constructorDemo(){
        //创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue。
        LinkedBlockingQueue<Integer> linkedBlockingQueue1  = new LinkedBlockingQueue<>();

        //创建一个具有给定(固定)容量的 LinkedBlockingQueue。
        LinkedBlockingQueue linkedBlockingQueue2 = new LinkedBlockingQueue(10);

        Collection<Integer> collection = new ArrayList();
        collection.add(1);
        //创建一个容量是 Integer.MAX_VALUE 的 LinkedBlockingQueue,最初包含给定 collection 的元素,元素按该 collection 迭代器的遍历顺序添加。
        LinkedBlockingQueue linkedBlockingQueue3 = new LinkedBlockingQueue(collection);
    }

LinkedBlockingDeque

双向阻塞队列,基于链表,链表默认容量Integer.Max ,可设定大小 ,内部ReentrantLock锁, 维护first,last.,可以当队列也用,可以拿来当栈用

    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque linkedBlockingDeque = new LinkedBlockingDeque();

        linkedBlockingDeque.putLast(0);
        linkedBlockingDeque.addFirst(1);
        //put -> putLast
        linkedBlockingDeque.put(2);

        System.out.println(linkedBlockingDeque);

        System.out.println("takeFirst " + linkedBlockingDeque.takeFirst());
        System.out.println("takeLast " + linkedBlockingDeque.takeLast());

        linkedBlockingDeque.push(3);
        linkedBlockingDeque.push(4);
        System.out.println(linkedBlockingDeque);
        System.out.println("pop " + linkedBlockingDeque.pop());
    }

PriorityBlockingQueue

内部基于数组实现,默认大小11,自动扩容(Integer最大值-8 某些虚拟机头信息怕报OutOfMemoryError),内部非公平锁, 每次获取元素通过compareTo()实现取元素顺序,同优先级顺序不确定

/**
 * @Author: xc
 * @Date: 2020/6/12
 * 优先级队列
 * 通过poll ,task 才有优先级获取元素. 同优先级顺序不确定 (遍历比较 compareTo), 自动扩容
 * 一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。
 * 虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。
 * 此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。
 *
 * 此类及其迭代器可以实现 Collection 和 Iterator 接口的所有可选 方法。
 * iterator() 方法中提供的迭代器并不 保证以特定的顺序遍历 PriorityBlockingQueue 的元素。
 * 如果需要有序地进行遍历,则应考虑使用 Arrays.sort(pq.toArray())。
 * 此外,可以使用方法 drainTo 按优先级顺序移除 全部或部分元素,并将它们放在另一个 collection 中。
 *
 * 在此类上进行的操作不保证具有同等优先级的元素的顺序。
 * 如果需要实施某一排序,那么可以定义自定义类或者比较器,比较器可使用修改键断开主优先级值之间的联系。
 * 例如,以下是应用先进先出 (first-in-first-out) 规则断开可比较元素之间联系的一个类。要使用该类,
 * 则需要插入一个新的 FIFOEntry(anEntry) 来替换普通的条目对象。
 */
    //4种构造方法
    @Test
    void constructorDemo(){
        //用默认的初始容量 (11) 创建一个 PriorityBlockingQueue,并根据元素的自然顺序对其元素进行排序。
        PriorityBlockingQueue<Integer> PriorityBlockingQueue1  = new PriorityBlockingQueue<>();

        //使用指定的初始容量创建一个 PriorityBlockingQueue,并根据元素的自然顺序对其元素进行排序。
        PriorityBlockingQueue<Integer> PriorityBlockingQueue2 = new PriorityBlockingQueue<>(10);

        Collection<Integer> collection = new ArrayList();
        collection.add(1);
        //创建一个包含指定 collection 元素的 PriorityBlockingQueue。
        PriorityBlockingQueue<Integer> PriorityBlockingQueue3 = new PriorityBlockingQueue<>(collection);

        //使用指定的初始容量创建一个 PriorityBlockingQueue,并根据指定的比较器对其元素进行排序。
        PriorityBlockingQueue<Integer> PriorityBlockingQueue4 = new PriorityBlockingQueue<Integer>(10 , (a,  b)->  {return a.compareTo(b);}  );
    }

DelayQueue

基于PriorityBlockingQueue实现的延迟队列,无界
队列元素需实现Delayed接口,延时时间到才能取出元素.

public class DelayQueueDemo {
    //两种构造方法
    @Test
    void constructorDemo(){
        //创建一个容量为 Integer.MAX_VALUE 的 DelayQueue。
        DelayQueue<Message> delayQueue1  = new DelayQueue<>();

        Collection<Message> collection = new ArrayList();
        collection.add(new Message(10L,"消息内容"));
        //创建一个容量为 Integer.MAX_VALUE 的 DelayQueue,最初包含给定 collection 的元素,以该 collection 迭代器的遍历顺序添加。
        LinkedBlockingDeque<Message> delayQueue2 = new LinkedBlockingDeque(collection);
    }

    public static void main(String[] args) throws InterruptedException {
        DelayQueue<Message> queue = new DelayQueue<>();
        queue.offer(new Message(100L,"消息1"));
        queue.offer(new Message(3000L,"消息2"));
        queue.offer(new Message(40L,"消息3"));
        queue.offer(new Message(150L,"消息4"));
        queue.offer(new Message(30L,"消息5"));

        for (;queue.size()>0;){
            System.out.println(queue.take());
        }
    }
}
//延迟队列元素实现Delayed
class Message implements Delayed {

    private Long expire;

    private Long delayTime;

    private String content;

    public Long getExpire() {
        return expire;
    }

    public Message(Long delayTime, String content) {
        this.delayTime = delayTime;
        this.content = content;
        this.expire = delayTime + System.currentTimeMillis();
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire-System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return (int)(this.expire - ((Message)o).getExpire());
    }

    @Override
    public String toString() {
        return "Message{" +
                "expire=" + expire +
                ", delayTime=" + delayTime +
                ", content='" + content + '\'' +
                '}';
    }
}

SynchronousQueue

内部没有维护容器, 采用cas保证安全. 使用SynchronousQueue必须绑定一个消费者,这样生产者才能插入成功
默认非公平 ,可以设置公平

     //SynchronousQueue队列  必须绑定一个消费者, 生产才会成功
    public static void main(String[] args) throws InterruptedException {
        //默认非公平,设置成公平模式
        SynchronousQueue synchronousQueue = new SynchronousQueue();
        for (int i = 0; i < 5; i++) {
            final int index = i;
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread() + " 生产产品"+ index );
                    synchronousQueue.put(index);
                    System.out.println(Thread.currentThread() + " 的产品"+ index +"已被消费");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
                Thread.sleep(100L);
        new Thread(()->{
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("消费产品: " + synchronousQueue.take());
                    Thread.sleep(10L);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }


LinkedTransferQueue

相对于一般的队列多了transfer和tryTransfer ,这两个方法和SynchronousQueue有点相似

public static void main(String[] args) throws InterruptedException {
        LinkedTransferQueue<Integer> linkedTransferQueue = new LinkedTransferQueue();
        for (int i = 0; i < 3 ; i++) {
            new Thread(()->{
                try {
                    System.out.println(linkedTransferQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        Thread.sleep(100L);
//        linkedTransferQueue.add(0);
        // 如果至少有一位消费者在等待,则返回 true
        System.out.println("hasWaitingConsumer " + linkedTransferQueue.hasWaitingConsumer());
        //-阻塞
        // 返回等待消费者人数的估计值
        System.out.println("hasWaitingConsumer " + linkedTransferQueue.getWaitingConsumerCount());
        // 如果有线程在task或者poll,就直接返回,否者 阻塞
        linkedTransferQueue.transfer(1);
        //相对于transfer 就是不会阻塞,直接返回 false
        linkedTransferQueue.tryTransfer(2);
        //相对于tryTransfer 在上一个等待
        linkedTransferQueue.tryTransfer(3, 1L, TimeUnit.SECONDS);

总结

1.阻塞队列基本实现方法
3种插入 add,offer,put
3种移除 remove, poll,take
遵循: 抛异常,立即返回(和有限时间等待),阻塞

2.不能插入null

3.线程安全保障: ReentrantLock

4.使用最好设定大小: 注意容量,不然出点意外内存就给你爆了

本篇只介绍了阻塞队列基础概念,实现类的原理, 数据结构, 固定方法,具体实现类方法没有说明,也比较简单,可以通过jdk文档,写下demo就基本明了了,我的demo代码:github地址.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值