BlockingQueue之PriorityBlockingQueue要点详解

  The implementation uses an array-based binary heap, with public
  operations protected with a single lock. However, allocation
  during resizing uses a simple spinlock (used only while not
  holding main lock) in order to allow takes to operate
  concurrently with allocation.  This avoids repeated
  postponement of waiting consumers and consequent element
  build-up. The need to back away from lock during allocation
  makes it impossible to simply wrap delegated
  java.util.PriorityQueue operations within a lock, as was done
  in a previous version of this class. To maintain
  interoperability, a plain PriorityQueue is still used during
  serialization, which maintains compatibility at the expense of
  transiently doubling overhead.

    这是官方给的对PriorityBlockingQueue的介绍,从上面我们可以得到一个很重要的信息,阻塞队列中的优先级排序是基于一个堆排序。
    在这之前,如果你接触BlockingQueue的话,你会提前接触ArrayBlockingQueue和LinkedBlockingQueue,我为什么没有写有关的博客呢?我承认懒是一部分,还有一点是他们大部分一样,而我们也会说它们的不一样的地方,在另外,我认为这个更有意思。
    BlockingQueue,支持添加,取出操作,另外它是线程安全的,学过操作系统的可能会对此有感觉,在讨论同步操作时我们见过类似的队列(生产者,消费者场景)生产者会产生数据并放入队列,消费者也会取出数据,但是当队列满了,生产者再往里面塞就会阻塞(也就是生产者线程阻塞),同理,当消费者取出数据时,如果队列为空,消费者线程也会阻塞。还有就是队列长度问题,上面我们说ArrayBlockingQueue是一个基于数组有具体大小的queue在构造的时候可以确定其大小;LinkedBlockingQueue是采取链表的blockingqueue,别看采取链表,确是有大小限制的也是在构造方法中给出的;PriorityBlockingQueue从名字就可以看出这个是优先级阻塞队列,采用的是数组实现队列,和前两种不一样的地方是它没有大小限制(默认大小是11 jdk1.7),有初始值哎,为什么说他没有限制大小,这里因为它只有一个Condition notNull;而没有Condition notFull;(下面会讲的这是啥)这导致他只会检查队列究竟是不是空,而不会检查队列满没满。内部就是通过toGrow()方法来进行扩展,当发现超过默认值就会调用。但是你可别以为这是一个多么令人兴奋的事,因为使用的要求也比较严格,因为此时我们需要注意不要让他无限增长否则会OutOfMemory,所以你必须确保生产者的生产速度必须比消费者快。
    那么这几种BlockingQueue的内部实现是怎样的?怎么实现的呢?因为本文主要讲的是PriorityBlockingQueue所以,前两者的我们把相同的地方只说一遍而且说的会比较简单。
    首先,你要思考几个问题:他们是如何实现线程安全的(放入,取出)如何判断队列空,队列满这两个状态。
    我们先说他们是如何判断队列非空,非满这两个有交集的状态。观看源码,发现是通过两个Condition,一个是nouNull,一个是notFull。Condition是条件变量,其方法Condition.await()和Condition.signal(),Condition.signalAll()作用等同于Object.wait(),Object.notify()且更加方便。谈到这又想说说Condition的作用,它通常是为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前一直挂起该线程(即让其“等待”)。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。 因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。 很显然,这个跟我们之前用的Object.wait()和Object.notify()相比好处是我们不在很专注与锁的管理(wait notify要求前者和后者是所得同一对象,且后者在唤醒前者时必须持有当前锁),而使用Condition只需要注意在绑定的对象的各种状态下进行await和signal操作就行了。(通常这个对象因为是多个线程共享,所以一般这个对象是一把锁,使用这把锁如lock.newCondition()来获取实例) 很显然,Condition的作用很适合应用于BlockingQueue这种(满则阻塞插入,空则阻塞取出)机制,当然,如果你觉得自己学得不错可以自己实现个类似ArrayBlockingQueue和LinkedBlockingQueue来实现自己的应用场景,当然,有现成的何必自己再弄~_~。 至于线程是如何实现的,翻看源码发现是使用ReetrantLock。这里面ArrayBlockingQueue和LinkedBlockingQueue的实现有不一样,为什么?因为在ArrayBlockingQueue中只有一个锁,这意味着一次只能对这队列进行一个操作,保证了队列的可见性和原子性,但这牺牲的就是效率;而LinkedBlockingQueue则是有两把锁putLock和takeLock分别对队列的put和take操作进行锁,那么两者就可以并发的进行。两者各有应用场景。
     好了,扯了半天,我们的做主角登场。。。PriorityBlockingQueue
     为了省点事下面我就简称PriorityBlockingQueue为“她”。
     她在实现线程安全上跟ArrayBlockingQueue实现一样,用了一个锁,不说了,我们要说说她的构造函数,上面已经说了,她有一个默认的队列大小11.所以构造函数有以下几种:
     PriorityBlockingQueue();PriorityBlockingQueue(int capacity);PriorityBlockingQueue(Collection collection);PriorityBlockingQueue(int capacity,Comparator comparator); 前两种不用说了,第三个,显然是将collection赋给它里面的PriorityQueue, 当然前提他们必须是提前实现Comparable接口,第四个,也很明显了吧,comparator的作用显然是排序,那么这样的话等下生产者添加元素到队列中就不需要对元素进行提前排序了,将排序规则comparator直接传给构造函数即可。谈到这,可能你还不知道为什么要排序,这就是“她”的作用,对你所想的东西进行一个优先级排序(有应用场景奥),但是在这个队列中并不是按顺序从小到大排序的。而是最小的放在队列首部,好吧,就是采用堆排序方法,既有利于获得每次最优先的,又不用完全排好序,效率提高了很多。它们的作用都十分明显,使用方法差不多,主要就是这几个方法:
      add(anObject); Inserts the anObject如果立即可行且不违反容量限制,则将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),并在成功时返回 true;如果当前没有空间可用,则抛出 IllegalStateException。
      offer(anobj);如果立即可行且不违反容量限制,则将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),并在成功时返回 true;如果当前没有空间可用,则返回 false。
     offer(anobject,longtime,TimeUnit);将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),必要时将在指定的等待时间内一直等待可用空间。
     put(anobject);将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),必要时将一直等待可用空间。(阻塞)
     poll(longtime,TimeUnit );  获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。
     take();获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
     remainingCapacity(); 返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的附加元素数量;如果没有内部限制,则返回   
     contains(Object o);如果此队列包含指定元素,则返回 true。

   下面再贴出“她”的代码
   public class PriorityBlockingQueue extends AbstractQueue
  implements BlockingQueue, java.io.Serializable {
  private static final long serialVersionUID = 5595510919245408276L;

你可能感兴趣的:(java基础,java并发)