浅谈SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue

分析这几个阻塞队列是自己在分析线程池的时候,发现Executors类提供的几个静态方法中,newCachedThreadPool()的阻塞队列使用的

是SynchronousQueue,而剩下两个用的是LinkedBlockingQueue,好奇为什么不同,于是便研究了一下,写下自己的理解:

在研究过程中,发现SynchronousQueue特别有意思,首先一些个人对这个类的理解,感觉这个同步队列好好玩,里面不存能东西,放不进去

也拿不出来。它的同步原理是,当生成者线程准备将元素放入这个队列时,如果这时没有消费者线程来,它就一直wait,等到有消费者线程来

了,消费者线程把元素取走,他就可以返回true了。同样的,当消费者准备从这个队列取元素时,如果这时正好有生产者线程来时,他就把元

素取走返回true,否则就一直在那等生产者线程来。下面来段正式的:

SynchronousQueue是无界的,是一种无缓冲的等待队列。它的方法返回值特有意思:

isEmpty()方法永远返回是true,remainingCapacity() 方法永远返回是0,remove()和removeAll() 方法永远返回是false,

iterator()方法永远返回空,peek()方法永远返回null。它非常适合于传递性设计,在这种设计中,在一个线程中运行的对象要

将某些信息、事件或任务传递给在另一个线程中运行的对象,它就必须与该对象同步。对于正在等待的生产者和使用者线程而言,此类支持可

选的公平排序策略。默认情况下不保证这种排序(即默认非公平)。但是,使用公平设置为 true 所构造的队列可保证线程以 FIFO 的顺序

进行访问。 公平通常会降低吞吐量,但是可以减小可变性并避免得不到服务(比如线程出现饥渴现象)。

         注意1:它一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。

注意2:它是线程安全的,是阻塞的。
  注意3:不允许使用 null 元素。
  注意4:公平排序策略是指调用put的线程之间,或take的线程之间。

LinkedBlockingQueue:

 LinkedBlockingQueue是无界的,是一个无界缓存的等待队列。

        基于链表的阻塞队列,内部维持着一个数据缓冲队列(该队列由链表构成)。当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

ArrayBlockingQueue:

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

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

分析完这三个,回到最初的问题,为什么newCachedThreadPool()用的是SynchronousQueue。

SynchronousQueue队列,一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作。所以,当我们提交第一个任务的时候,是加入不了队列的,这就满足了,一个线程池条件“当无法加入队列的时候,且任务没有达到maxsize时,我们将新开启一个线程任务”。所以我们的maxsize是big big。时间是60s,当一个线程没有任务执行会暂时保存60s超时时间,如果没有的新的任务的话,会从cache中remove掉。
 

参考文章:http://blog.csdn.net/hudashi/article/details/7076814

http://blog.csdn.net/mn11201117/article/details/8671497#java

 

 

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值