JUC之BlockingQueue

介绍

BlockingQueue即阻塞队列,它是基于ReentrantLock,依据它的基本原理,我们可以实现Web中的长连接聊天功能,当然其最常用的还是用于实现生产者与消费者模式,大致如下图所示:
在这里插入图片描述
在Java中,BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的。

阻塞与非阻塞

入队

add(E e) :将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException
offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false–>不阻塞
put(E e):如果队列满了,一直阻塞,直到队列不满了或者线程被中断–>阻塞、
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:–>阻塞

被唤醒

等待时间超时

当前线程被中断

出队

poll():如果没有元素,直接返回null;如果有元素,出队
take():如果队列空了,一直阻塞,直到队列不为空或者线程被中断–>阻塞
remove(Object o) :从此队列中移除指定元素的单个实例(如果存在)
poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:

被唤醒

等待时间超时

当前线程被中断

Java线程池中常用的BlockingQueue的实现

在这里插入图片描述

ArrayBlockingQueue源码分析

主要乘员变量介绍

    /** 队列存储的地方 */
    final Object[] items;
    /** 当前队列下一个take, poll, peek or remove的下标 */
    int takeIndex;
    /** 队列put,offer或add的下标 */
    int putIndex;
    /**队列现有总数量*/
    int count;
    /** 所有入口主锁 */
    final ReentrantLock lock;
    /** takes的等待条件 */
    private final Condition notEmpty;
    /** puts的等待条件 */
    private final Condition notFull;
    /**
     * Shared state for currently active iterators, or null if there
     * are known not to be any.  Allows queue operations to update
     * iterator state.
     * 当前活动迭代器的共享状态;如果存在,则为null 众所周知不是。 允许队列操作更新
       迭代器状态。
     */
    transient Itrs itrs = null;

构造函数介绍

	/**固定长度队列,非公平锁*/
	
	public ArrayBlockingQueue(int capacity) {
	      this(capacity, false);
    }

    /**
     * 固定长度队列,fair:ture公平锁,fair: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();
    }

    /**
     * 固定长度,fair:ture公平锁,fair:false非公平锁,可加入,小于等于capacity的集合
     */
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

通过 putIndex = (i == capacity) ? 0 : i;可以看出,当队列i==capacity时即队列添加到数组的最后一位的时候,将新增索引置为0,(当索引为0的值取出时,就可以根据putIndex继续添加了)

put和take主要函数介绍

    /**
     * 如果队列满了,notFull.await()一直阻塞,直到队列不满了或者线程被中断-->阻塞
     */
    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();
        }
    }
    /**
     * 向队列中插入数据,能够执行到该步骤说明,count<this.items.length
     * 当下一个putIndex == items.length时,将数组位置指向0继续插入,插入完成
     * notEmpty.signal()唤醒正在等待take的操作去去队列中的值
     */
    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();
    }

    /**
     *  从队列中取数据,能够执行到该步骤说明,count>0
     * 当下一个takeIndex == items.length时,将数组位置指向0继续取,取出后有空位
     * notFull.signal()唤醒正在等待put的操作向队列中存值
     */
    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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值