参考及引用声明:
Java多线程进阶(三一)—— J.U.C之collections框架:BlockingQueue接口
不怕难之BlockingQueue及其实现
ReentrantLock(重入锁)功能详解和应用演示
Java之BlockingQueue
BlockingQueue深入解析-BlockingQueue看这一篇就够了
ThreadPoolExecutor线程池解析与BlockingQueue的三种实现
一、BlockingQueue塞队列/h2>
BlockingQueue即阻塞队列,一个指定长度的队列(“先进先出”)**,如果队列满了,添加新元素的操作会被阻塞等待,直到有空位为止**。同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元素为止。
通过加“锁”实现线程安全的一个“容器”。
场景类比:还是上厕所的例子。某单位的厕所有3个厕位,一开始,都在“等待”被上,先后进来5个人,先进来的3个人抢占到了位置,并且关门上“锁”,其他两个人就要排队“等待” 。这个时候,厕所就相当于一个长度为3的阻塞队列。
常用于实现生产者与消费者模式,“生产者”和“消费者”是相互独立的,两者之间的通信需要依靠一个队列。这个队列,其实就是所谓的“阻塞队列”。“阻塞队列”的最大好处就是解耦,使“生产者”和“消费者”之间解耦,互不影响的。
扩展:生产者-消费者模式 Producer-Consumer Pattern
生产者和消费者在为不同的处理线程,生产者必须将数据安全地交给消费者,消费者进行消费时,如果生产者还没有建立数据,则消费者需要等待。
类比的例子里,高铁站可以看成是生产者,一直源源不断“产生”要坐的士的乘客。而的士,就可以看成消费者,一直在“消费”那些乘客。
Channel从Producer参与者处接受Data参与者,并保管起来,并应Consumer参与者的要求,将Data参与者传送出去。为确保安全性,Producer参与者与Consumer参与者要对访问共享互斥。
出处: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接口的实现类都必须是线程安全的,实现类一般通过“锁”保证线程安全;
1,ArrayBlockingQueue 数组阻塞队列
参考:Java多线程进阶(三二)—— J.U.C之collections框架:ArrayBlockingQueue
- 基于定长数组的阻塞队列,只有一个锁(入队和出队都用同一个锁),按照先进先出(FIFO)的原则对元素进行排序。
- 可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。
- 这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。 一旦创建了这样的缓存区,就不能再增加其容量。(数组大小在构造函数指定,而且从此以后不可改变。 )
- 试图从空队列中检索元素将导致类似阻塞,直到BlocingkQueue进了新货才会被唤醒。
扩展:公平锁 与 非公平锁
公平锁:加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得。
非公平锁:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。
更多的是直接使用非公平锁:非公平锁比公平锁性能高5-10倍,因为公平锁需要在多核情况下维护一个队列,如果当前线程不是队列的第一个无法获取锁,增加了线程切换次数。
- 部分源码及分析:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!