阻塞队列| 利用流程图看懂ArrayBlockingQueue源码

线程-阻塞队列



前言

阻塞队列是线程池十分重要的一个部分,当大量的任务到来时,线程池会把多出来的任务,暂存在阻塞队列中。它有好的地方,那它坑的地方你了解吗?


在这里插入图片描述

提示:以下是本篇文章正文内容

一、ArrayBlockingQueue简介

是一个数组实现的环形队列,经常会使用并发容器用于存储多线程间的共享数据,这样不仅可以保证线程安全,还可以简化各个线程操作。

二、ArrayBlockingQueue队列的原理

利用了Lock锁的Condition通知机制进行阻塞控制。
核心:一把锁,两个条件

//数据元素数组
final Object[] items;
//下一个待取出元素索引
int takeIndex;
//下一个待添加元素索引
int putIndex;
//元素个数
int count;
//内部锁
final ReentrantLock lock;
//消费者
private final Condition notEmpty;
//生产者
private final Condition notFull;  

public ArrayBlockingQueue(int capacity, boolean fair) {
    ...
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

1. 一图看懂put与take源码

在这里插入图片描述

1.1 put总结

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) {
        // 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();
    }

我们在对流程图进行文字的总结

  1. 拿到线程竞争lock锁,拿到了lock锁的线程进入下一步,没有拿到lock锁的线程自旋竞争锁。
  2. 判断阻塞队列是否满了,如果满了,则调用await方法阻塞这个线程,notFull(生产者)挂起,最后释放lock锁,等待被消费者线程唤醒。
  3. 如果没有满,则调用enqueue方法将元素put进阻塞队列。
  4. 唤醒一个标记为notEmpty(消费者)的线程。

1.2 take总结

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() {
        // 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;
    }

我们在对流程图进行文字的总结

  1. 拿到线程竞争lock锁,拿到了lock锁的线程进入下一步,没有拿到lock锁的线程自旋竞争锁。
  2. 判断阻塞队列是否为空,如果是空,则调用await方法阻塞这个线程,notEmpty(消费者)挂起,最后释放lock锁,等待被生产者线程唤醒。
  3. 如果没有空,则调用dequeue方法。
  4. 唤醒一个标记为notFull(生产者)的线程

总结

通过看源码,我们发现阻塞队列的实现还是不难的,主要是利用并发工具Condition来进行控制,通过阻塞(await)与唤醒(signal)线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值