阻塞队列

Queue接口
在Queue接口中,除了继承Collection接口中定义的方法外,它还分别额外地定义插入、删除、查询这3个操作,其中每一个操作都以两种不同的形式存在,每一种形式都对应着一个方法。
在这里插入图片描述

  1. add方法在将一个元素插入到队列的尾部时,如果出现队列已经满了,那么就会抛出IllegalStateException,而使用offer方法时,如果队列满了,则添加失败,返回false,但并不会引发异常。
  2. remove方法是获取队列的头部元素并且删除,如果当队列为空时,那么就会抛出NoSuchElementException。而poll在队列为空时,则返回一个null。
  3. element方法是从队列中获取到队列的第一个元素,但不会删除,但是如果队列为空时,那么它就会抛出NoSuchElementException。peek方法与之类似,只是不会抛出异常,而是返回false。

BlockingQueue接口
BlockingQueue是JDK1.5出现的接口,它在原来的Queue接口基础上提供了更多的额外功能:当获取队列中的头部元素时,如果队列为空,那么它将会使执行线程处于等待状态;当添加一个元素到队列的尾部时,如果队列已经满了,那么它同样会使执行的线程处于等待状态。

BlockingQueue 阻塞队列,排队拥堵,首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下图所示:
在这里插入图片描述

  • 线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素
  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞
  • 当阻塞队列是满时,从队列中添加元素的操作将会被阻塞

前面我们在说Queue接口时提到过,它针对于相同的操作提供了2种不同的形式,而BlockingQueue更夸张,针对于相同的操作提供了4种不同的形式。
该四种形式分别为:

  1. 抛出异常
  2. 返回一个特殊值(可能是null或者是false,取决于具体的操作)
  3. 阻塞当前执行直到其可以继续
  4. 当线程被挂起后,等待最大的时间,如果一旦超时,即使该操作依旧无法继续执行,线程也不会再继续等待下去。

在这里插入图片描述

  1. BlockingQueue中是不允许添加null的,该接受在声明的时候就要求所有的实现类在接收到一个null的时候,都应该抛出NullPointerException。
  2. BlockingQueue是线程安全的,因此它的所有和队列相关的方法都具有原子性。但是对于那么从Collection接口中继承而来的批量操作方法,比如addAll(Collection e)等方法,BlockingQueue的实现通常没有保证其具有原子性,因此我们在使用的BlockingQueue,应该尽可能地不去使用这些方法。
  3. BlockingQueue主要应用于生产者与消费者的模型中,其元素的添加和获取都是极具规律性的。但是对于remove(Object o)这样的方法,虽然BlockingQueue可以保证元素正确的删除,但是这样的操作会非常响应性能,因此我们在没有特殊的情况下,也应该避免使用这类方法。

ArrayBlockingQueue
在这里插入图片描述

public E take() throws InterruptedException {
   final ReentrantLock lock = this.lock;
  /*
  尝试获取锁,如果此时锁被其他线程锁占用,那么当前线程就处于Waiting的状态。
  注意:当方法是支持线程中断响应的如果其他线程此时中断当前线程,那么当前线程就会抛出InterruptedException
 */
  lock.lockInterruptibly();
  try {
  /*
  如果此时队列中的元素个数为0,那么就让当前线程wait,并且释放锁。
  注意:这里使用了while进行重复检查,是为了防止当前线程可能由于其他
 未知的原因被唤醒。
  (通常这种情况被称为"spurious wakeup")
  */
  while (count == 0)
  notEmpty.await();
  //如果队列不为空,则从队列的头部取元素
  return extract();
} finally {
  //完成锁的释放
  lock.unlock();
}
}
	public void put(E e) throws InterruptedException {
		// 首先检查元素是否为空,否则抛出NullPointerException
		checkNotNull(e);
		final ReentrantLock lock = this.lock;
		// 进行锁的抢占
		lock.lockInterruptibly();
		try {
			/*
			 * 当队列的长度等于数组的长度,此时说明队列已经满了,这里同样 使用了while来方式当前线程被"伪唤醒"。
			 */
			while (count == items.length)
				// 则让当前线程处于等待状态
				notFull.await();
			// 一旦获取到锁并且队列还未满时,则执行insert操作。
			insert(e);
		} finally {
			// 完成锁的释放
			lock.unlock();
		}
	}

	// 检查元素是否为空
	private static void checkNotNull(Object v) {
		if (v == null)
			throw new NullPointerException();
	}

	// 该方法的逻辑非常简单
	private void insert(E x) {
		// 将当前元素设置到putIndex位置
		items[putIndex] = x;
		// 让putIndex++
		putIndex = inc(putIndex);
		// 将队列的大小加1
		++count;
		// 唤醒其他正在处于等待状态的线程
		notEmpty.signal();
	}
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页