java点滴之阻塞队列:PriorityBlockingQueue,SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue

一PriorityBlockingQueue:

一个无界的阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞检索的操作。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会失败(导致 OutOfMemoryError)。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(因为这样做会抛出 ClassCastException)。
 PriorityBlockingQueue()
          用默认的初始容量 (11) 创建一个 PriorityBlockingQueue,并根据元素的自然顺序排序其元素(使用Comparable)。 PriorityBlockingQueue(Collection<? extends E> c) 
          创建一个包含指定集合中元素的 PriorityBlockingQueue。 PriorityBlockingQueue(int initialCapacity) 
          使用指定的初始容量创建一个 PriorityBlockingQueue,并根据元素的自然顺序排序其元素(使用 Comparable)。PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) 
          使用指定的初始容量创建一个 PriorityBlockingQueue,并根据指定的比较器排序其元素。


此类每次offer元素,都会有一个fixup操作,也就是排序,如果没有构造的时候传入自己实现的比较器,就采用自然排序,否则采用比较器规则,进行二分查找,比较,保持列头是比较器希望的那个最大或则最小元素。
 
private void fixUp(int k) {
        if (comparator == null) {
            while (k > 1) {
                int j = k >> 1;
                if (((Comparable<E>)queue[j]).compareTo((E)queue[k]) <= 0)
                    break;
                Object tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
                k = j;
            }
        } else {
            while (k > 1) {
                int j = k >>> 1;
                if (comparator.compare((E)queue[j], (E)queue[k]) <= 0)
                    break;
                Object tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
                k = j;
            }
        }
    }

SynchronousQueue

SynchronousQueue是无界的,是一种无缓冲的等待队列,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加;可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是 isEmpty()方法永远返回是true,remainingCapacity() 方法永远返回是0,remove()和removeAll() 方法永远返回是false,iterator()方法永远返回空,peek()方法永远返回null。

        声明一个SynchronousQueue有两种不同的方式,它们之间有着不太一样的行为。公平模式和非公平模式的区别:如果采用公平模式:SynchronousQueue会采用公平锁,并配合一个FIFO队列来阻塞多余的生产者和消费者,从而体系整体的公平策略;但如果是非公平模式(SynchronousQueue默认):SynchronousQueue采用非公平锁,同时配合一个LIFO队列来管理多余的生产者和消费者,而后一种模式,如果生产者和消费者的处理速度有差距,则很容易出现饥渴的情况,即可能有某些生产者或者是消费者的数据永远都得不到处理。




三LinkedBlockingQueue

        LinkedBlockingQueue是无界的,是一个无界缓存的等待队列。也是事件队列最常用的。

        基于链表的阻塞队列,内部维持着一个数据缓冲队列(该队列由链表构成)。当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。

        LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

private class EventQueue {
        /* event queue */
        private BlockingQueue<InputEvent> queuedEvents = new LinkedBlockingQueue<InputEvent>();
        private final int timeOut =500;
	 protected boolean eventQueueStatus = true;
        public InputEvent getNextEvent() {
            while (eventQueueStatus) {
                InputEvent queuedEvent;
                try {
                    queuedEvent = queuedEvents.poll(timeOut, TimeUnit.MILLISECONDS);
                    if (queuedEvent != null) {
                        // dispatch the event
                        return queuedEvent;
                    } else {
                        return null;
                    }
                } catch (InterruptedException e) {
                  
                    return null;
                }
            }
	    return null;
        }

        
        public void addEvent(InputEvent ev) {
            try {
                if(ev != null) {
                  queuedEvents.offer(ev, timeOut, TimeUnit.MILLISECONDS );
                }
            } catch (InterruptedException e) {
               
            } catch (IllegalArgumentException e) {
                
            }
        }
    }



四ArrayListBlockingQueue 

         ArrayListBlockingQueue是有界的,是一个有界缓存的等待队列。
         基于数组的阻塞队列,同LinkedBlockingQueue类似,内部维持着一个定长数据缓冲队列(该队列由数组构成)。ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。
         ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。

         ArrayBlockingQueue和LinkedBlockingQueue是两个最普通、最常用的阻塞队列,一般情况下,处理多线程间的生产者消费者问题,使用这两个类足以。


以上两个都是BlockingQueue基类的子类,基类提供的方法有:

 获取数据:
  poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,
    取不到时返回null;
  poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,
    队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
  take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到
    BlockingQueue有新的数据被加入; 
  drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数), 
    通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。 
  放入数据:
  offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,
    则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
  offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中
    加入BlockingQueue,则返回失败。
  put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断
    直到BlockingQueue里面有空间再继续.


你可能感兴趣的:(多线程,线程,性能)