三.多线程JUC篇-3.27 LinkedBlockingQueue与ConcurrentLinkedQueue的区别

1.阻塞队列与非阻塞队列

队列是一种常见的数据结构,先进先出,在日常工作中有着高频率的使用。在 Java 中应用也更为广泛,是生产消费模型首选的数据结构,简化了开发,解耦生产与消费的关系。Java中 Queue 可以分为阻塞队列和非阻塞队列。

1.1 阻塞队列

非阻塞队列也就是一般的队列,没有阻塞队列的两个阻塞功能。其主要方法如下:

  • boolean add(E e):将元素e插入到队列末尾,插入成功,返回true;插入失败(即队列已满),抛出异常;

  • boolean offer(E e):将元素e插入到队列末尾,插入成功,则返回true;插入失败(即队列已满),返回false;

  • E remove():移除队首元素,若移除成功,则返回true;移除失败(队列为空),则会抛出异常;

  • E poll():获取队首元素并移除,若队列不为空,则返回队首元素;否则返回null;

  • E element():获取队首元素并不移除元素,若队列不为空,则返回队首元素;否则抛出异常;

  • E peek():获取队首元素并不移除元素,若队列不为空,则返回队首元素;否则返回null;

1.2 阻塞队列

队列一般都有着两个阻塞操作,即插入与取出。

  • 当队列满时,会阻塞元素的插入,直到队列有空闲时停止阻塞,新元素才可以继续插入。

  • 当队列为空时,移除元素的线程会一直被阻塞等待,直到队列中有元素时才可以继续取出。

除拥有普通队列的方法之外,阻塞队列提供了另外4个常用的方法:

  • put(E e):向队尾插入元素,若队列已满,则被阻塞等待,直到有空闲才继续插入。

  • take():从队首取出元素,若队列为空,则被阻塞等待,直到有元素才继续取出。

  • offer(E e,long timeout, TimeUnit unit):向队尾插入元素,若队列已满,则计时等待,当时间期限达到时,若队列还是满的,则返回false;若等待在期限内,队列空闲,则插入成功,返回true;

  • poll(long timeout, TimeUnit unit):从队首取出元素,如果队列空,则计时等待,当时间期限达到时,若队列还是空的,则返回null;若等待在期限内,队列中有元素,否则返回取得的元素;

2.LinkedBlockingQueue

由于 LinkedBlockingQueue通过ReentrantLock实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定长度的话,默认是 Integer.MAX_VALUE,即无界队列。LinkedBlockingQueue比普通队列多出的方法参见上边阻塞队列方法。

3.ConcurrentLinkedQueue

ConcurrentLinkedQueue 也是 Queue 的一个安全实现,是非阻塞队列,其方法参见上边非阻塞队列。队列中元素按 FIFO 原则进行排序。采用 CAS操作,来保证元素的一致性。

4.二者区别

  • 首先二者都是线程安全的得队列,都可以用于生产与消费模型的场景。

  • LinkedBlockingQueue是阻塞队列,其好处是:多线程操作共同的队列时不需要额外的同步,由于具有插入与移除的双重阻塞功能,对插入与移除进行阻塞,队列会自动平衡负载,从而减少生产与消费的处理速度差距。

  • 由于LinkedBlockingQueue有阻塞功能,其阻塞是基于锁机制实现的,当有多个线程消费时候,队列为空时消费线程被阻塞,有元素时需要再唤醒消费线程,队列元素可能时有时无,导致用户态与内核态切换频繁,消耗系统资源。从此方面来讲,LinkedBlockingQueue更适用于多线程插入,单线程取出,即多个生产者与单个消费者。

  • ConcurrentLinkedQueue非阻塞队列,采用 CAS+自旋操作,解决多线程之间的竞争,多写操作增加冲突几率,增加自选次数,并不适合多写入的场景。当许多线程共享访问一个公共集合时,ConcurrentLinkedQueue 是一个恰当的选择。从此方面来讲,ConcurrentLinkedQueue更适用于单线程插入,多线程取出,即单个生产者与多个消费者。

  • 总之,对于几个线程生产与几个线程消费,二者并没有严格的规定, 只有谁更适合

  • 从其它博客拿过来的
    (1) 适用阻塞队列的好处:多线程操作共同的队列时不需要额外的同步,另外就是队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。
    (2) 当许多线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。
    (3) LinkedBlockingQueue 多用于任务队列,ConcurrentLinkedQueue 多用于消息队列
    (4) 多个生产者,对于LBQ性能还算可以接受;但是多个消费者就不行了mainLoop需要一个timeout的机制,否则空转,cpu会飙升的。LBQ正好提供了timeout的接口,更方便使用;如果CLQ,那么我需要收到处理sleep
    (5) 单生产者,单消费者 用 LinkedBlockingqueue
          多生产者,单消费者 用 LinkedBlockingqueue
          单生产者 ,多消费者 用 ConcurrentLinkedQueue
          多生产者 ,多消费者 用 ConcurrentLinkedQueue

猜你喜欢

转载自blog.csdn.net/weixin_42868638/article/details/112271574