java8的七个阻塞队列

七种阻塞队列

ArrayBlockingQueue

数组实现的有界阻塞队列,此队列按照先进先出FIFO原则对元素进行排序

LinkedBlockingQueue

链表实现的有界阻塞队列,此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则

PriorityBlockingQueue

支持优先级排序的无界阻塞队列,默认情况下元素采用自然排序升序排列。也可以自定义类实现compareTo()方法来指定元素排序规则,或者初始化PriorityBlockingQueue时,指定构造参数Comparetor来对元素进行排序

DelayQueue

优先级队列实现的无界阻塞队列

SynchronousQueue

不存储元素的阻塞队列,每一个put操作必须必须等待一个tack操作,否则不能继续添加元素

LinkedTransferQueue

链表实现的无界阻塞队列

LinkedBlockingDeque

链表实现的双向阻塞队列

阻塞队列的操作方法

阻塞队列中提供四种处理方式

插入操作

  1. add(e) : 添加元素到队列中,如果队列满了,继续插入元素就会报错,IllgalStateException
  2. offer(e): 添加元素到队列,即时返回插入成功的状态,如果成功则返回true
  3. put(e): 当阻塞队列满了之后,生产者继续通过put添加元素,队列会一直阻塞生产者线程,直到可用
  4. offer(e,time,unit):当阻塞队列满了之后继续添加元素,生产者线程会被阻塞指定时间,如果超时,线程直接退出

移除操作

  1. remove() :当队列为空时,调用remove会返回false,如果元素移除成功,则返回true
  2. poll(): 当队列存在元素时,则取出,如果不存在则返回null
  3. take(): 基于阻塞的方式从队列中取出元素,如果队列为空,则take方法会一直阻塞,知道队列中有新的数据可以消费
  4. poll(time, unit) : 带超时机制的获取数据,如果队列为空则会等待指定的时间再去获取元素返回。‘

ArrayBlockingQueue原理分析

capacity:表示数组的长度,也就是队列的长度
fair:表示是否为公平的阻塞队列,默认情况下构造的是非公平的阻塞队列。
其中第三个构造方法就不解释了,它提供了接收一个几个作为数据初始化的方法

ArrayBlockingQueue内部的阻塞队列是通过重入锁ReenterLock和Condition条件队列实现的,所以ArrayBlockingQueue中的元素存在公平访问与非公平访问的区别,对于公平访问队列,被阻塞的线程可以按照阻塞的先后顺序访问队列,即先阻塞的线程先访问队列。而非公平队列,当队列可用时,阻塞的线程将进入争夺访问资源的竞争中,也就是说谁先抢到谁就执行,没有固定的先后顺序

public ArrayBlockingQueue(int capacity) { 
	this(capacity, false);
	}
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();//初始化非满等待队列
	 }
  1. Add方法

    以add方法作为入口,在add方法中会调用父类的add方法,也就是AbstractQueue.如果看源码看得比较多的话,一般这种写法都是调用父类的模版方法来解决通用性问题

public boolean add(E e) { 
	return super.add(e);
	}

从父类的add方法可以看到,这里做了一个队列是否满了的判断,如果队列满了直接抛出一个异常

public boolean add(E e) {
	 if (offer(e)) 
	 	return true; 
	 else throw new IllegalStateException("Queue full");}
  1. offer方法
    add方法最终还是调用offer方法来添加数据,返回一个添加成功或者失败的布尔值反馈
    这段代码做了几个事情
    1. .判断添加的数据是否为空
    2. 添加重入锁
    3. 判断队列长度,如果队列长度等于数组长度,表示满了直接返回false
    4. 否则,直接调用enqueue将元素添加到队列中
    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();
        }
    }

enqueue
这个是最核心的逻辑,方法内部通过putIndex索引直接将元素添加到数组items

    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x; // //通过putIndex对数据赋值
        if (++putIndex == items.length) //当putIndex等于数组长度时,将putIndex重置为0
            putIndex = 0;
        count++;  //记录队列元素的个数
        notEmpty.signal();  // **唤醒处于等待状态下的线程,表示当前队列中的元素不为空,如果存在消费者线程阻塞,就可以开始取出元素**  
    }
  1. put方法
    put方法和add方法功能一样,差异是put方法如果队列满了,会阻塞。这个在最开始的时候说过。接下来看一下它的实现逻辑
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();// //这个也是获得锁,但是和lock的区别是,这个方法优先允许在等待时由其他线程调用等待线程的interrupt方法来中断等待直接返回。而lock方法是尝试获得锁成功后才响应中断
        try {
            while (count == items.length)
                notFull.await();//队列满了的情况下,当前线程将会被notFull条件对象挂起加到等待队列中
                // 当其他线程调用了condition的signal方法后,condition状态的节点会从等待队列转移到同步队列中,等待获取同步锁。
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

在这里插入图片描述

  1. take方法

take方法是一种阻塞获取队列中元素的方法
它的实现原理很简单,有就删除没有就阻塞,注意这个阻塞是可以中断的,如果队列没有数据那么就加入notEmpty条件队列等待(有数据就直接取走,方法结束),如果有新的put线程添加了数据,那么put操作将会唤醒take线程,执行take操作。

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await(); //如果队列为空的情况下,直接通过await方法阻塞
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

如果队列中添加了元素,那么这个时候,会在enqueue中调用notempty.signal唤醒take线程来获得元素

dequeue方法
这个是出队列的方法,主要是删除队列头部的元素并发返回给客户端
takeIndex,是用来记录拿数据的索引值

    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];//默认获取0位置的元素
        items[takeIndex] = null;//将该位置的元素设置为空
        if (++takeIndex == items.length)//这里的作用也是一样,如果拿到数组的最大值,那么重置为0,继续从头部位置开始获取数据
            takeIndex = 0;
        count--;//记录元素个数递减
        if (itrs != null)
            itrs.elementDequeued(); //同时更新迭代器中的元素数据
        notFull.signal(); //触发因为队列满了以后导致的被阻塞的线程
       

在这里插入图片描述
element 和peek不会移除元素,poll会移除元素

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EmineWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值