ArrayBlockingQueue 与LinkedBlockingQueue
来源:互联网 发布:强生隐形眼镜 知乎 编辑:程序博客网 时间:2024/06/01 11:53
目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlockingQueue,以便日后灵活使用。
1. 在Java的Concurrent包中,添加了阻塞队列BlockingQueue,用于多线程编程。BlockingQueue的核心方法有:
boolean add(E e) ,把 e 添加到BlockingQueue里。如果BlockingQueue可以容纳,则返回true,否则抛出异常。
boolean offer(E e),表示如果可能的话,将 e 加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。
void put(E e),把 e 添加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻塞直到BlockingQueue里面有空间再继续。
E poll(long timeout, TimeUnit unit) ,取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null。
E take() ,取走BlockingQueue里排在首位的对象,若BlockingQueue为空,则调用此方法的线程被阻塞直到BlockingQueue有新的数据被加入。
int drainTo(Collection<? super E> c) 和 int drainTo(Collection<? super E> c, int maxElements) ,一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取 数据的个数),通过该方法,可以提升获取数据效率,不需要多次分批加锁或释放锁。
2. BlockingQueue常用的四个实现类
ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.
2) LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的
3) PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
4) SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的.
3. ArrayBlockingQueue源码分析
ArrayBlockingQueue是一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素,队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致放入操作受阻塞;试图从空队列中检索元素将导致类似阻塞。
ArrayBlockingQueue创建的时候需要指定容量capacity(可以存储的最大的元素个数,因为它不会自动扩容)。其中一个构造方法为:
ArrayBlockingQueue类中定义的变量有:
使用数组items来存储元素,由于是循环队列,使用takeIndex和putIndex来标记put和take的位置。可以看到,该类中只定义了一个锁ReentrantLock,定义两个Condition对象:notEmputy和notFull,分别用来对take和put操作进行所控制。注:本文主要讲解put()和take()操作,其他方法类似。put(E e)方法的源码如下。进行put操作之前,必须获得锁并进行加锁操作,以保证线程安全性。加锁后,若发现队列已满,则调用notFull.await()方法,如当前线程陷入等待。直到其他线程take走某个元素后,会调用notFull.signal()方法来激活该线程。激活之后,继续下面的插入操作。
insert(E e) 方法如下:take()方法代码如下。take操作和put操作相反,故不作详细介绍。extract() 方法如下:小结:进行put和take操作,共用同一个锁对象。也即是说,put和take无法并行执行!4. LinkedBlockingQueue 源码分析
基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
LinkedBlockingQueue 类中定义的变量有:
该类中定义了两个ReentrantLock锁:putLock和takeLock,分别用于put端和take端。也就是说,生成端和消费端各自独立拥有一把锁,避免了读(take)写(put)时互相竞争锁的情况。enqueue(E e)方法如下:take()方法代码如下。take操作和put操作相反,故不作详细介绍。dequeue()方法如下:
小结:take和put操作各有一把锁,可并行读取。
- ArrayBlockingQueue与LinkedBlockingQueue
- ArrayBlockingQueue 与LinkedBlockingQueue
- LinkedBlockingQueue与ArrayBlockingQueue性能比较
- ArrayBlockingQueue与LinkedBlockingQueue的使用及区别
- 简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- 简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- 简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue区别
- 简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- ArrayBlockingQueue 和LinkedBlockingQueue
- ArrayBlockingQueue、LinkedBlockingQueue、ConcurrentLinkedQueue
- ArrayBlockingQueue和LinkedBlockingQueue
- 多线程-队列ArrayBlockingQueue 、LinkedBlockingQueue
- SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- ArrayBlockingQueue 和 LinkedBlockingQueue 对比
- 简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- 简析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
- ArrayBlockingQueue 和LinkedBlockingQueue
- Android图表库MPAndroidChart(十四)——在ListView种使用相同的图表
- 设计模式六大原则(2):里氏替换原则
- __ATTRIBUTE__ 你知多少?
- 记录:django学习第一课:
- Android自定义控件之高仿京东购物车添加或者减少商品数量功能
- ArrayBlockingQueue 与LinkedBlockingQueue
- webpack plugin
- Struts2 运行流程分析
- rpm包制作之——nginx
- 学习笔记---三种循环
- 手机号码归属地批量查询软件使用说明
- 【PHP】用isset检测一个变量是否存在
- struts2配置常量<constant name="struts.devMode">,将值修改为true(开发模式有什么好处)的好处
- servlet中getServletContext().getRealPath("/");的路径