JAVA多线程之——ArrayBlockingQueue

30 篇文章 0 订阅

ArrayBlockingQueue

ArrayBlockingQueue队列是BlockingQueue的一个子类.block是阻塞的意思。它是一个阻塞队列,array说明它的底层是基于数组实现。数组是有大小的,所以它是一个有(边)界的队列。综上,ArrayBlockingQueue是一个有界的阻塞队列。
BlockingQueue
BlockingQueue接口有三个删除方法和三个添加方法
添加方法

  • boolean add(E e); 添加一个元素,成功则返回true,添加失败返回false.如果因为队列容量满了而添加失败抛出IllegalStateException异常
  • boolean offer(E e);添加成功返回true,添加失败返回false
  • void put(E e) throws InterruptedException; 添加元素,添加成功返回true,如果队列满了就一直阻塞知道队列可以添加元素。可能抛出InterruptedException异常。
    删除方法
  • E take() throws InterruptedException;删除队列头,并返回其值。如果队列为空,则一直阻塞,知道队列有数据。在阻塞过程中,如果线程中断,那么会抛出InterruptedException异常
  • E poll() throws InterruptedException;删除队列头部元素,并返回其值.如果队列为空返回null
  • boolean remove(Object o);删除制定的元素对象,删除成功返回true,删除失败返回false.
    ArrayBlockingQueue基本原理
/** 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中,有一个独占锁,和这个锁的两个条件对象。一个notEmpy和notFull.通过这个可重入的独占锁,和条件就可以并发控制。

add实现

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

调用了父类AbstractQueue队列的实现方法,如下:

public boolean add(E e) {
      if (offer(e))
          return true;
      else
          throw new IllegalStateException("Queue full");
  }

add方法调用的offer()方法。因此看offer源码就行。
offer实现

 public boolean offer(E e) {
       checkNotNull(e);//检查插入对象是否为空,如果为空抛出空指针异常
       final ReentrantLock lock = this.lock;
       lock.lock();//上锁
       try {
           if (count == items.length)//元素的个数等于数组长度,队列已满返回false
               return false;
           else {
               insert(e);//队列没满就直接插入 
               return true;//插入成功返回true
           }
       } finally {
           lock.unlock();
       }
   }

空检查

 private static void checkNotNull(Object v) {
        if (v == null)
            throw new NullPointerException();
    }

insert方法

private void insert(E x) {
   items[putIndex] = x; //插入元素
   putIndex = inc(putIndex); //当前插入数据索引加1.如果满了就归0
   ++count;//元素个数加1
   notEmpty.signal();//通知队列为空时候,被阻塞的取线程。
  }

put方法实现

public void put(E e) throws InterruptedException {
      checkNotNull(e);//空检查,为空抛出空指针异常
      final ReentrantLock lock = this.lock;
      lock.lockInterruptibly();//获取锁。如果线程被中断,抛出异常
      try {
          while (count == items.length)//队列已满,阻塞当前线程,加入条件等待队列。
              notFull.await();
          insert(e);//队列没满插入数据。并通知在等待取数线程。
      } finally {
          lock.unlock();
      }
  }

poll方法实现

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();//获取锁
    try {
        return (count == 0) ? null : extract();//如果队列为空,返回null.否则调用extract()方法 该方法源码看下面。
    } finally {
        lock.unlock();
    }
}

extract方法

private E extract() {
     final Object[] items = this.items;
     E x = this.<E>cast(items[takeIndex]);//获取取索引上的元素
     items[takeIndex] = null;//清空数据
     takeIndex = inc(takeIndex);//取数索引加1.如果队列空了就归0
     --count;//元素个数减1
     notFull.signal();//通知等待添加元素的线程。
     return x;//返回元素
    }

remove方法


public boolean remove(Object o) {
     if (o == null) return false;
     final Object[] items = this.items;//
     final ReentrantLock lock = this.lock;
     lock.lock();//上锁
     try {
         for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {
             if (o.equals(items[i])) { //从取数索引开始遍历,如果相等调用removeAt方法如果
                 removeAt(i);
                 return true;//删除成功就返回true
             }
         }
         return false;//删除失败就返回true
     } finally {
         lock.unlock();
     }
 }

removeAt方法

void removeAt(int i) {
      final Object[] items = this.items;
      // if removing front item, just advance
      if (i == takeIndex) { //如果删除的元素是取索引上的值,删除元素,取索引加1
          items[takeIndex] = null;
          takeIndex = inc(takeIndex);
      } else {
          // slide over all others up through putIndex.
          for (;;) {//如果不是就从删除元素的索引的下一个位置往前移。直到放索引位置,更新放索引退出循环。
              int nexti = inc(i);
              if (nexti != putIndex) { //如果被删除的元素下一个不是放索引
                  items[i] = items[nexti]; //将下一个元素前移一个。(循环移动。当前需要删除的元素被它下一个覆盖。所以不需要置空)
                  i = nexti;
              } else {
                  items[i] = null;//将最后一个元素移除。
                  putIndex = i;//更新取索引
                  break;
              }
          }
      }
      --count;
      notFull.signal();//通知被阻塞的放线程
  }

可能上面会有一点点模糊。加入有一个队列【1,2,3,4,5,6,null,null…..】如果移除2.那么在
循环第一次 [1,3,3,4,5,6,null……]
第二次 [1,3,4,4,5,6,null…..]
第三次 [1,3,4,5,5,6,null……]
第四次 [1,3,4,5,6,6,null….]
第五次 [1,3,4,5,6,null,……]
最后一次把最后那个重复的删除,其实就是一个数组移动的过程。
take方法实现

public E take() throws InterruptedException {
     final ReentrantLock lock = this.lock;
     lock.lockInterruptibly();//上锁如果线程被中断,抛出异常
     try {
         while (count == 0) //队列为空,则阻塞当前线程,加入等待队列
             notEmpty.await();
         return extract(); //调用extract方法。
     } finally {
         lock.unlock();
     }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值