Java并发集合之ArrayBlockingQueue源码解析

ArrayBlockingQueue是一个线程安全的有界阻塞队列,基于数组实现,使用ReentrantLock和Condition进行同步控制。队列通过offer、take和put等方法实现元素的添加和移除,当队列满或空时,线程会进行等待。公平锁和非公平锁可在构造时选择,默认为非公平锁。
摘要由CSDN通过智能技术生成

ArrayBlockingQueue 简介

ArrayBlockingQueue是线程安全的有界阻塞队列。

  • 线程安全是指ArrayBlockingQueue通过内部互斥锁实现了多线程并发访问时对资源竞争的互斥
  • 有界队列是指ArrayBlockingQueue是有长度限制的
  • ArrayBlockingQueue是FIFO(先进先出)队列,从尾部插入头部返回

ArrayBlockingQueue原理与数据结构

ArrayBlockingQueue内部是通过数组实现的有界队列

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /** The queued items */
    final Object[] items;
    
    /** Main lock guarding all access */
    final ReentrantLock lock;
    
    /** Condition for waiting takes */
    private final Condition notEmpty;

    /** Condition for waiting puts */
    private final Condition notFull;
  • ArrayBlockingQueue是通过Object数组保存数据的,数组长度即是在初始化时定义的队列长度
  • ArrayBlockingQueue通过内部的ReentrantLock对象实现多线程的互斥,ReentrantLock又分为公平锁和非公平锁,在ArrayBlockingQueue创建的时候可以指定,默认为非公平锁
  • ArrayBlockingQueue通过内部的Condition对象实现达到精确控制的目的,当队列为空时可以通过notEmpty.await()方法进行等待,当队列插入数据时通过notEmpty.signal()方法唤醒等待线程

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();
    }
  • items是保存队列数据的数组,其大小为队列的容量
  • fair表示ReentrantLock的锁类型,true为公平锁,false为非公平锁

变更方法

  • offer方法
public boolean offer(E e) {
		// 检查插入对象是否为null,是null的话抛出空指针异常
        checkNotNull(e);
        // 获取锁并加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        	// 如果队列已满则返回false
            if (count == items.length)
                return false;
            else {
            	// 队列不满则插入数据
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }
  • enqueue方法
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上的等待线程
        notEmpty.signal();
    }

ArrayBlockingQueue通过对数组的循环写入和读取达到有界队列的目的,通过两个值标记队列的队头(takeIndex)和队尾(putIndex)

  • take方法
public E take() throws InterruptedException {
		// 获取锁对象
        final ReentrantLock lock = this.lock;
        // 加锁,如果线程中断则抛出InterruptedException
        lock.lockInterruptibly();
        try {
        	// 若队列为空则一直等待
            while (count == 0)
                notEmpty.await();
            // 从队列头部取出数据
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

take方法从队列头部取出一条数据,如果队列为空则会一直等待

  • dequeue方法
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;
    }

以上源码为JDK1.8的实现,其中增加了共享迭代器的功能,共享迭代器维护了一个当前实例现存的所有迭代器对象的引用,并且在从队列中取出数据后进行同步的状态更新,以达到队列和迭代器共享数据的目的,但是实现复杂性会高很多,这里不做过多的赘述了,有兴趣的话可以自己去了解一下

  • put方法
public void put(E e) throws InterruptedException {
		// 检查插入对象是否为null,是null的话抛出空指针异常
        checkNotNull(e);
        // 加锁,如果线程中断则抛出InterruptedException
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
        	// 如果队列已满则等待
            while (count == items.length)
                notFull.await();
            // 插入数据
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

ArrayBlockingQueue的迭代器方法实现非常复杂,因为涉及到与队列共用数据的缘故,代码篇幅也特别长,这里不做过多的展开

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值