BlockingQueue——从入门到深究

参考及引用声明:

Java多线程进阶(三一)—— J.U.C之collections框架:BlockingQueue接口

不怕难之BlockingQueue及其实现

ReentrantLock(重入锁)功能详解和应用演示

Java之BlockingQueue

BlockingQueue深入解析-BlockingQueue看这一篇就够了

ThreadPoolExecutor线程池解析与BlockingQueue的三种实现

一、BlockingQueue塞队列/h2>

BlockingQueue即阻塞队列,一个指定长度的队列(“先进先出”)**,如果队列满了,添加新元素的操作会被阻塞等待,直到有空位为止**。同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元素为止。

通过加“锁”实现线程安全的一个“容器”。

场景类比:还是上厕所的例子。某单位的厕所有3个厕位,一开始,都在“等待”被上,先后进来5个人,先进来的3个人抢占到了位置,并且关门上“锁”,其他两个人就要排队“等待” 。这个时候,厕所就相当于一个长度为3的阻塞队列。

常用于实现生产者与消费者模式,“生产者”和“消费者”是相互独立的,两者之间的通信需要依靠一个队列。这个队列,其实就是所谓的“阻塞队列”。“阻塞队列”的最大好处就是解耦,使“生产者”和“消费者”之间解耦,互不影响的。

扩展:生产者-消费者模式 Producer-Consumer Pattern

生产者和消费者在为不同的处理线程,生产者必须将数据安全地交给消费者,消费者进行消费时,如果生产者还没有建立数据,则消费者需要等待。

类比的例子里,高铁站可以看成是生产者,一直源源不断“产生”要坐的士的乘客。而的士,就可以看成消费者,一直在“消费”那些乘客。

BlockingQueue——从入门到深究

Channel从Producer参与者处接受Data参与者,并保管起来,并应Consumer参与者的要求,将Data参与者传送出去。为确保安全性,Producer参与者与Consumer参与者要对访问共享互斥。

BlockingQueue——从入门到深究 BlockingQueue——从入门到深究

出处:https://segmentfault.com/a/1190000015558655

 

二:ReentrantLock(可重入锁)

BlockingQueue它是基于ReentrantLock。我们需要先了解ReentrantLock(可重入锁)。

ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。

1,与synchronized的对比:

jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock。虽然在性能上ReentrantLock和synchronized没有什么区别,但ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。

ReentrantLock(灵活)和synchronized(隐式,自动)都是独占锁,只允许线程互斥的访问临界区

ReentrantLock和(重入时要却确保重复获取锁的次数必须和重复释放锁的次数一样)synchronized(可以放在被递归执行,不用担心释放问题)都是可重入的

2,默认(和synchronized一样)是非公平锁

也可通过传参new ReentrantLock(true)实现公平。

3,ReentrantLock实现简单的阻塞队列

(类包括ReentrantLock,LinkedList,用于唤醒和等待的Condition对象),参见ReentrantLock(重入锁)功能详解和应用演示

  • 可以响应中断的获取锁的方法可以用来解决死锁问题。

三:BlockingQueue源码剖析

BlockingQueue继承了Queue接口,可以看到,对于每种基本方法,“抛出异常”和“返回特殊值”的方法定义和Queue是完全一样的。BlockingQueue只是增加了两类和阻塞相关的方法:put(e)take()offer(e, time, unit)poll(time, unit)

同时,BlockingQueue队列中不能包含null元素。

BlockingQueue——从入门到深究
  • BlockingQueue的核心方法:

 

四:常用实现类及部分源码解析

BlockingQueue接口的实现类都必须是线程安全的,实现类一般通过“锁”保证线程安全

BlockingQueue——从入门到深究 BlockingQueue——从入门到深究

BlockingQueue——从入门到深究

1,ArrayBlockingQueue 数组阻塞队列

参考:Java多线程进阶(三二)—— J.U.C之collections框架:ArrayBlockingQueue

  • 基于定长数组的阻塞队列,只有一个锁(入队和出队都用同一个锁),按照先进先出(FIFO)的原则对元素进行排序。
  • 可以控制对象的内部锁是否采用公平锁,默认采用非公平锁

BlockingQueue——从入门到深究
  • 这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。 一旦创建了这样的缓存区,就不能再增加其容量。(数组大小在构造函数指定,而且从此以后不可改变
  • BlockingQueue——从入门到深究试图向已满队列中放入元素会导致放入操作受阻塞,直到BlockingQueue里有新的唤空间才会被醒继续操作;
  • 试图从空队列中检索元素将导致类似阻塞,直到BlocingkQueue进了新货才会被唤醒。

扩展:公平锁 与 非公平锁

  • 公平锁:加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得

  • 非公平锁:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。

更多的是直接使用非公平锁:非公平锁比公平锁性能高5-10倍,因为公平锁需要在多核情况下维护一个队列,如果当前线程不是队列的第一个无法获取锁,增加了线程切换次数。

  • 部分源码及分析:

来源:陈软件

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年1月7日
下一篇 2020年1月8日

相关推荐