通过源码看阻塞队列之ArrayBlockingQueue

ArrayBlockingQueue是使用独占锁(ReentrantLock)实现的阻塞队列,ArrayBlockingQueue与LinkedBlockingQueue的不同之处,就是ArrayBlockingQueue底层的数据结构使用的是数组。且添加数据和获取数据用的都是同一个独占锁,所以读写并没有分离,读写操作不能并行执行。

 

属性源码分析:

    /** 数组用于存储数据元素 */
    final Object[] items;
    /** 获取元素时数据元素下标 */
    int takeIndex;
    /** 添加元素时数据元素下标 */
    int putIndex;
    /** 队列中元素个数 */
    int count;
    /** 全局锁,添加数据和获取数据都使用该独占锁 */
    final ReentrantLock lock;
    /** 获取数据时,队空线程阻塞时的条件队列 */
    private final Condition notEmpty;
    /** 添加数据时,队满线程阻塞时的条件队列 */
    private final Condition notFull;

 

常用方法:

添加数据

boolean   offer(E  e):从队尾插入数据,如果队满则遗弃数据,返回false,不会造成阻塞。

void   put(E e):从队尾插入数据,如果队满,线程阻塞,并把线程放入notFull条件队列

 

获取数据:

E   poll():获取并弹出队头元素,如果队空,则返回null,不会造成阻塞。

E   take():获取并弹出队头元素,如果队空,线程阻塞,并不线程放入notEmpty条件队列

E   peek():获取队头元素,但不弹出,如果队空,则返回null。

 

其他方法

int  size():获取元素个数

 

常用方法源码分析:

boolean   offer(E  e):向队列尾部插入一个数据元素,如果队列有空闲则插入成功返回true,如果队满,则遗弃当前元素返回false。如果插入元素为null,则抛出NullPointerException。

    public boolean offer(E e) {
        //1)、数据元素为空时,抛出NullPointerException异常
        checkNotNull(e);
        //2)、获取锁资源
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //如果队满,遗弃数据元素返回false
            if (count == items.length)
                return false;
            else {
                //插入数据元素,返回true
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

    //数据元素为空时,抛出NullPointerException异常
    private static void checkNotNull(Object v) {
        if (v == null)
            throw new NullPointerException();
    }
    //入队
    private void enqueue(E x) {
        //数据元素插入队列
        final Object[] items = this.items;
        items[putIndex] = x;
        //计算下一个元素存放的位置
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();  //唤醒notEmpty条件队列中的一个线程
    }

 

void   put(E e):往队列尾部插入数据元素,如果数据为null,则抛出NullPointerException,如果队列空闲,插入成功并返回,如果队满,则线程阻塞,把线程放入notFull条件队列,如果该线程在其他线程中设置了中断表示,则会抛出InterruptedException。

    public void put(E e) throws InterruptedException {
        //如果插入元素为null,则抛出NullPointerException
        checkNotNull(e);
        //获取独占锁lock 
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //如果队满,线程阻塞放入notNull条件队列
            while (count == items.length)
                notFull.await();
            //数据元素入队
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

 

E   poll():获取并弹出队头元素,如果队空,则返回null,不会造成阻塞。

    public E poll() {
        //获取独占锁lock 
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //队空则返回null,否则获取并弹出队头元素
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock(); //释放锁资源
        }
    }
      
    //获取并弹出队头元素
    private E dequeue() {
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        //获取队头数据元素
        E x = (E) items[takeIndex];
        //队头数据元素设置为null,弹出队头元素
        items[takeIndex] = null;
        //队头指针计算,队列元素个数减1
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal(); //唤醒notFull条件队列中的一个线程
        return x;
    }

 

E   take():获取并弹出队头元素,如果队空,线程阻塞,并不线程放入notEmpty条件队列。如果该线程在其他线程中调用了线程中断表示,则会抛出InterruptedException异常

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //队空,线程阻塞,把线程放入notEmpty条件队列
            while (count == 0)
                notEmpty.await();
            //队列不为空,获取并弹出队头数据元素
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

 

E   peek():获取队头元素,但不弹出,如果队空,则返回null。 源码就是根据takeIndex指针获取数据元素。

    public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return itemAt(takeIndex); // null when queue is empty
        } finally {
            lock.unlock();
        }
    }

 

int  size():获取元素个数。加锁是避免线程间的可见性问题,从主内存中获取数据元素。同时也阻塞了其他线程的入队和出队操作,获取的数据元素是精确的。

    public int size() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }

图解ArrayBlockingQueue

 

参考:《java并发编程之美》

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值