阻塞队列LinkedBlockingQueue源码学习与对比

前上一篇详细学习了ArrayBlockingQueue,今天学习LinkedBlockingQueue源码并与之对比。

LinkedBlockingQueue总结

同样先直接总结LinkedBlockingQueue相关的特性,再根据源码来进行说明,它的主要特性如下:

1、他底层用链表实现数据存储;

2、他是一个FIFO无界阻塞队列。数据最大长度默认Intenger的最大值Integer.MAX_VALUE,也可以设置长度;

重要属性介绍

主要属性如下图:

1属性介绍.jpg

LinkedBlockingQueue有一个内部类Node,用来组成它存储数据的链表,Node的item数据用来存储数据,next表示下一个节点,尾节点的next是null。

capacity容量表示队列最多可存储的数据量,初始化的时候设置,默认是Integer.MAX_VALUE;

count表示当前存储数据的数量,它是AtomicInteger类型(可以想想为什么?);

head链表的头节点,它的item不存放数据;

tail链表的尾节点,它的next为null,也就是没有下一个节点;

takeLock与notEmpty控制take方法的阻塞;

putLock与notFull控制put方法的阻塞;

从上面属性可以猜出来LinkedBlockingQueue是用两个锁来控制数据的读和写,相当于把读和写分开,可以同时存在读和写操作,所以count有可能同时存在多个线程修改,有线程安全问题所以用AtomicInteger。

最关键的两个私有方法

同样LinkedBlockingQueue也有两个相同的关键方法,具体代码和解释如下图:
2关键源码.png

LinkedBlockingQueue的enqueue和dequeue方法比ArrayBlockingQueue的更加简单;

enqueue方法就是把新的节点放到last的next,然后把节点设置成尾节点。

dequeue是先拿到头节点next节点作为first节点,然后之前的头节点就的next设置成了自己,就脱离了链表,下次GC就会被回收掉。

然后first就作为head,把item拿出来后设置为null,所以头节点是不存储数据的,并且是由下一个节点升级来的。

LinkedBlockingQueue的这两个方法比较简单,但是也可以看出它实现了FIFO先进先出。

主要方法介绍

LinkedBlockingQueue与ArrayBlockingQueue一样都是继承至 AbstractQueue并实现 BlockingQueue接口,所以他们提供的方法都差不多,功能也类似,主要实现不同,上一篇已经详细讲了,这里就只看一下take和put方法的实现。

put方法源码如下图:

3put方法源码.png

一共分四步:

把数据封装成Node节点,获取put锁;

验证队列是否已满,满则阻塞线程;

如果没有满则把节点加到队列中,count加1,并获取加之前的数量;

如果现在数量还是小于最大容量则唤醒阻塞的put线程,如果之前数量是0则唤醒take线程;

take方法源码如下图:

4take方法源码.png

take方法和put方法差不多,主要分以下四步:

首先获取take锁;

判断队列中是否有数据,如果没有则阻塞线程;

调用dequeue方法获取数据,并把count减1;

如果获取前队列数量大于1则说明还有数据,可以唤醒其他take线程,如果获取前队列数量等于最大容器数量则说明有可能有阻塞的put线程,需要唤醒;

可以看到put和take方法很简单,都是先获取到对于的锁,然后根据队列数量判断是否阻塞,然后再添加或者获取数据,最后根据唤醒对应线程;

与ArrayBlockingQueue对比(重点)

LinkedBlockingQueue与ArrayBlockingQueue是非常相似的类,通过对比能够更体现他们的优缺点,主要对比如下:

从基础属性对比LinkedBlockingQueue底层实现是链表,ArrayBlockingQueue是数组。ArrayBlockingQueue初始化需要一个数组,而LinkedBlockingQueue是动态数量的Node对象;所以ArrayBlockingQueue需要预先分配内存,而LinkedBlockingQueue不用,如果预计需要缓存的数据很多的,那么ArrayBlockingQueue一开始就需要很大一块数据。

但是ArrayBlockingQueue添加元素更快,因为它只是要要保存的对象的引用放到数组对应位置,而LinkedBlockingQueue需要创建一个Node对象;同时在获取后这个Node对象变成了垃圾,在读写很大的情况会多出很多垃圾,可能会影响程序的性能;

ArrayBlockingQueue用一个锁控制读写,LinkedBlockingQueue用两个锁分别控制读写。因为ArrayBlockingQueue为了避免数据被覆盖,而LinkedBlockingQueue不用担心这种情况,可以同时支持读写,而ArrayBlockingQueue同一时刻支持一个操作,所以LinkedBlockingQueue吞吐量更高。

所以ArrayBlockingQueue会占用固定的内存,所以不能支持太大的缓存,但是每次操作延迟更加低,而LinkedBlockingQueue不用暂用一个初始化的内存,但是每次操作消耗的内存更大,不过同时支持读写所以吞吐量更高。

但是在使用LinkedBlockingQueue时,如果使用默认大小且当生产速度大于消费速度时候,有可能会内存溢出。

总结

LinkedBlockingQueue与ArrayBlockingQueue提供的功能完全相同,但是他们底层的实现不同,所以侧重点不同,在使用中可以根据情况选择。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
Java程序员日常学习笔记

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值