java中的线程安全队列

2019/08/14 12:11
阅读数 131

在并发编程种,有时候需要使用线程安全的队列,如果实现一个线程安全的队列有两种方式:阻塞算法、非阻塞算法。使用阻塞算法的队列可以用一个锁(出入队列用同一把锁),或两个锁(入队和出队用不同的锁),非阻塞的实现方式则可以用循环CAS的方式实现。

一 非阻塞方式实现线程安全的队列

ConcurrentLinkedQueue

 ConcurrentLinkedQueue由head节点和tail节点组成,每个节点node由节点元素item和指向下一个节点next的引用组成。当我们增加一个元素时,它会添加到队列的末尾,当我们取一个元素时,它会返回一个队列头部的元素。

 

虽然ConcurrentLinkedQueue的性能很好,但是在调用size()方法的时候,会遍历一遍集合,对性能损害较大,执行很慢,因此应该尽量的减少使用这个方法,如果判断是否为空,最好用isEmpty()方法

 ConcurrentLinkedQueue不允许插入null元素,会抛出空指针异常。

 ConcurrentLinkedQueue是无界的,所以使用时,一定要注意内存溢出的问题。即对并发不是很大中等的情况下使用,不然占用内存过多或者溢出,对程序的性能影响很大,甚至是致命的。

 

 

二 阻塞方式实现线程安全的对列

JDK7提供了7个阻塞队列,如下。
口 ArrayBlockingqueue:ー个由数组结构组成的有界阻塞队列。
口 LinkedBlockingqueue:一个由链表结构组成的有界阻塞队列。
口 PriorityBlockingqueue:一个支持优先级排序的无界阻塞队列。
口 Delayqueue:ー个使用优先级队列实现的无界阻塞队列。
口 Synchronousqueue:一个不存储元素的阻塞队列。
口 Linkedtransferqueue:ー个由链表结构组成的无界阻塞队列。
口 LinkedblockingDeque:ー个由链表结构组成的双向阻塞队列。

 

ArrayBlockingQueue

 

LinkedBlockingQueue 

ConcurrentLinkedQueue、ArrayBlockingQueue、LinkedBlockingQueue 区别及使用场景

ArrayBlockingQueue extends AbstractQueue implements BlockingQueue 
LinkedBlockingQueue extends AbstractQueue implements BlockingQueue
ConcurrentLinkedQueue extends AbstractQueue implements Queue

ConcurrentLinkedQueue基于CAS的无锁技术,不需要在每个操作时使用锁,所以扩展性表现要更加优异,在常见的多线程访问场景,一般可以提供较高吞吐量。

LinkedBlockingQueue内部则是基于锁,并提供了BlockingQueue的等待性方法。

 

ArrayBlockingQueue是初始容量固定的阻塞队列,我们可以用来作为数据库模块成功竞拍的队列,比如有10个商品,那么我们就设定一个10大小的数组队列。

ConcurrentLinkedQueue使用的是CAS原语无锁队列实现,是一个异步队列入队的速度很快出队进行了加锁,性能稍慢。

LinkedBlockingQueue也是阻塞的队列,入队和出队都用了加锁,当队空的时候线程会暂时阻塞。

 


 

LinkedBlockingQueue与ArrayBlockingQueue的异同

相同:

1、LinkedBlockingQueue和ArrayBlockingQueue都实现了BlockingQueue接口;

2、LinkedBlockingQueue和ArrayBlockingQueue都是可阻塞的队列 

  内部都是使用ReentrantLock和Condition来保证生产和消费的同步;

  当队列为空,消费者线程被阻塞;当队列装满,生产者线程被阻塞;

使用Condition的方法来同步和通信:await()和signal()

不同:

1、由上图可以看出,他们的锁机制不同

  LinkedBlockingQueue中的锁是分离的,生产者的锁PutLock,消费者的锁takeLock

  而ArrayBlockingQueue生产者和消费者使用的是同一把锁;

2、他们的底层实现机制也不同

  LinkedBlockingQueue内部维护的是一个链表结构

在生产和消费的时候,需要创建Node对象进行插入或移除,大批量数据的系统中,其对于GC的压力会比较大

而ArrayBlockingQueue内部维护了一个数组

在生产和消费的时候,是直接将枚举对象插入或移除的,不会产生或销毁任何额外的对象实例

 3、构造时候的区别

  LinkedBlockingQueue有默认的容量大小为:Integer.MAX_VALUE,当然也可以传入指定的容量大小

ArrayBlockingQueue在初始化的时候,必须传入一个容量大小的值

  看其提供的构造方法就能知道

4、执行clear()方法

  LinkedBlockingQueue执行clear方法时,会加上两把锁

5、统计元素的个数

 LinkedBlockingQueue中使用了一个AtomicInteger对象来统计元素的个数

 

 ArrayBlockingQueue则使用int类型来统计元素

 

 

https://www.jb51.net/article/90899.htm(好)

https://blog.csdn.net/jameshadoop/article/details/52729796

 

阻塞队列的实现

https://segmentfault.com/a/1190000000373535

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部