并发(多线程)------>BlockingQueue接口源码解读与实现类ArrayBlockingQueue实现类(生产者消费者)

前言:BlockingQueue 是一个线程安全的队列,它提供add,offe,put,poll,take,drop等方法,可以安全的使用在生产者和消费者模型当中作为一个中间的队列,而且基于BlockingQueue接口实现的类可以便生产边消费,不冲突  例子在最后

* A {@link java.util.Queue} that additionally supports operations
* that wait for the queue to become non-empty when retrieving an
* element, and wait for space to become available in the queue when
* storing an element.

归纳:是一个支持  取元素时 等待队列非空 存元素时 等待队列空间可用(队列已满)  的队列

* <p>{@code BlockingQueue} methods come in four forms, with different ways
* of handling operations that cannot be satisfied immediately, but may be
* satisfied at some point in the future:
* one throws an exception, the second returns a special value (either
* {@code null} or {@code false}, depending on the operation), the third
* blocks the current thread indefinitely until the operation can succeed,
* and the fourth blocks for only a given maximum time limit before giving
* up.  These methods are summarized in the following table:

归纳:Blocking有四种形式,不同的处理方式不能立即满足但可能在未来某个时间满足操作

形式1:抛出异常

形式2:返回特殊值,可以是null或false

形式3:无限期阻塞当前线程,直到操作成功(后面讲的某些方法就是如此)

形式4:只阻塞给定的最大时间限制,然后放弃

 

BlockingQueue方法总结:

1:Throws exception     Special value      Blocks       Times out

2:Insert        add(e)      put(e)     off(Object,long,TimeUnit) ------> offer(e,time,unit)

3:Remove    remove()     poll()      take()      poll(long,TimeUnit)------>poll(time,unit)

4:Examine    element()     peek()     

add方法,有容量的时候可以插入,插入成功返回true,容量不够会返回异常IleagalStateException,最好使用offer

offer    方法 在队列中插入一个元素  成功返回true  不成功返回false.    

put 方法   将元素插入该队列,如有必要则等待有空位。

offer(e,timeout,TimeUnit)   将指定的元素插入该队列,如有必要,等待到指定的等待时间,以获得可用空间,(可以这样理解)TimeUnit是timeout的单位,源码如下图

1. take() 检索并移除该队列的头部,必要时等待,直到一个元素变得可用。

2. poll(timeout,TimeUnit)检索并删除该队列的头部,如果需要等待一个元素可用,则需等待至指定的等待时间。

3. remainingCapacity() 返回该队列在理想情况下(在没有内存或资源限制的情况下)可以接受的额外元素的数量,或者{@code Integer.MAX_VALUE}。如果没有内在的限制。注意,你不能总是通过检查{@code remainingCapacity}来判断插入元素的尝试是否会成功,因为有可能是另一个线程要插入或删除一个元素。

4. remove(e) 如果元素存在 ,从这个队列中删除一个指定元素的实例。 如果这个队列包含了指定的元素,返回true 否则返回false

5. contains(e) 是否存元素 存在返回true  否则返回false

6. dropTo(Collection<E> c) 从这个队列中删除所有可用的元素,并将它们添加到给定的集合中。 这个操作可能比重复轮询这个队列更有效率。 当试图将元素添加到集合{@code c}时遇到失败,当相关的异常被抛出时,可能会导致元素既不在、也不在或在两个集合中。 试图将一个队列排到自身的结果是{@code IllegalArgumentException}。此外,如果指定的集合在操作过程中被修改,那么这个操作的行为是未定义的。

7. dropTo(Collection<E>c,int maxElements) 从这个队列中最多删除给定数量的可用元素,并将它们添加到给定的集合中。 当试图将元素添加到集合{@code c}时遇到的失败可能会导致元素既不在集合中,也不在集合中,或者在两个集合中都不在。 试图将一个队列排到自己身上会导致{@code IllegalArgumentException}。此外,如果指定的集合在操作过程中被修改,那么这个操作的行为是未定义的。

 

 

 

下面是关于源码中注释的翻译  不想看的话直接看后面的小结

>A {@code BlockingQueue} does not accept {@code null} elements.
* Implementations throw {@code NullPointerException} on attempts
* to {@code add}, {@code put} or {@code offer} a {@code null}.  A
* {@code null} is used as a sentinel value to indicate failure of
* {@code poll} operations.

 BlockingQueue 不接受null的元素   试图add,put,offer对null操作时,可能会报出(NullPointerException)  

null被用作哨兵来指示poll操作的失败

* <p>A {@code BlockingQueue} may be capacity bounded. At any given
* time it may have a {@code remainingCapacity} beyond which no
* additional elements can be {@code put} without blocking.
* A {@code BlockingQueue} without any intrinsic capacity constraints always
* reports a remaining capacity of {@code Integer.MAX_VALUE}.

BlockingQueue可能有容量限制,有一个 remainingCapacity ,  超过这个值,就不能再put而不被阻塞。

如果一个BlockingQueue没有容量限制,就被报告显示Interger.MAX_VALUE(这里就说我们生产者消费者的那个队列如果不设置一个容量的话  就会报出  剩余容量是Interger.MAX_VALUE)

* <p>{@code BlockingQueue} implementations are designed to be used
* primarily for producer-consumer queues, but additionally support
* the {@link java.util.Collection} interface.  So, for example, it is
* possible to remove an arbitrary element from a queue using
* {@code remove(x)}. However, such operations are in general
* <em>not</em> performed very efficiently, and are intended for only
* occasional use, such as when a queued message is cancelled.

BlockingQueue的实现被设计为主要用于生产者-消费者队列,但也支持Collection接口。 因此,例如,可以使用remove(x)从队列中删除一个任意元素。然而,这类操作一般来说不是非常有效地执行,而且只打算偶尔使用,例如当队列中的消息被取消时。

* <p>{@code BlockingQueue} implementations are thread-safe.  All
* queuing methods achieve their effects atomically using internal
* locks or other forms of concurrency control. However, the
* <em>bulk</em> Collection operations {@code addAll},
* {@code containsAll}, {@code retainAll} and {@code removeAll} are
* <em>not</em> necessarily performed atomically unless specified
* otherwise in an implementation. So it is possible, for example, for
* {@code addAll(c)} to fail (throwing an exception) after adding
* only some of the elements in {@code c}.

BlockingQueue的实现是线程安全的。 所有的队列方法都是使用内部锁或其他形式的并发控制来原子化地实现它们的效果。然而,bulk,Collection操作 addAll、containsAll、retainAll和removeAll除非在实现中另有规定,否则不一定要以原子方式执行。因此,例如,addAll(c)有可能在只添加了c中的部分元素后失败(抛出异常)。

* <p>A {@code BlockingQueue} does <em>not</em> intrinsically support
* any kind of &quot;close&quot; or &quot;shutdown&quot; operation to
* indicate that no more items will be added.  The needs and usage of
* such features tend to be implementation-dependent. For example, a
* common tactic is for producers to insert special
* <em>end-of-stream</em> or <em>poison</em> objects, that are
* interpreted accordingly when taken by consumers.

<p>一个BlockingQueue并不内在地支持任何类型的close;或;shutdown操作来指示不再添加项目。 这种功能的需求和使用往往取决于实现。例如,一种常见的策略是生产者插入特殊的<em>end-of-stream</em>或<em>poison</em>对象,当消费者取用这些对象时,会有相应的解释。

 

小结上面源码中的注释

1. BlockingQueue 不接受null的元素   试图add,put,offer对null操作时,可能会报出(NullPointerException)  

2. 如果一个BlockingQueue没有容量限制,就被报告显示Interger.MAX_VALUE(这里就说我们生产者消费者的那个队列如果不设置一个容量的话  就会报出  剩余容量是Interger.MAX_VALUE)

3. BlockingQueue的实现是线程安全的。 所有的队列方法都是使用内部锁或其他形式的并发控制来原子化地实现它们的效果。

4. bulk,Collection操作 addAll、containsAll、retainAll和removeAll除非在实现中另有规定,否则不一定要以原子方式执行。因此,例如,addAll(c)有可能在只添加了c中的部分元素后失败(抛出异常)。

 

基于BlockingQueue接口的定义,已经有了众多的实现BlockingQueue的类  例如ArrayBlockingQueue等,可以在IEDA中查看BlockingQueue就可已找到他们了  下面基于ArrayBlockingQueue实现生产者消费者模型

 

生产者消费者基于ArrayBlockingQueue(BlockingQueue的一个实现类)

用到的方法   offer/put(在ArrayBlockingQueue实现中,这两个方法如果添加对象元素不成功,可以等待,直到我们的队列中有了空位再进行)     take()会自动消费队列中的元素,也就是队列中的元素减一,同时返回消费的元素的值

下面是一个典型的生产者-消费者场景的使用实例。一个BlockingQueue可以安全的用于多个生产者和消费者

public class MyTest {
    public static void main(String[] args) {
        int capacity=10;//队列是20的容量
        BlockingQueue q = new ArrayBlockingQueue(capacity);
        Producer p = new Producer(q);
//        Producer p1 = new Producer(q);
//        Producer p2 = new Producer(q);
//        Producer p3 = new Producer(q);
        Consumer c1 = new Consumer(q);
        Consumer c2 = new Consumer(q);
        System.out.println("开始");
        new Thread(p).start();
        new Thread(c1,"消费者1").start();
        new Thread(c2,"消费者2").start();

    }
}
class Producer implements Runnable{
    private BlockingQueue queue ;
    Producer(BlockingQueue q){queue=q;}
    public void run(){
            try {
                while(true){
                    Thread.sleep(1);
                    Object product=produce();//生产的元素
                    queue.offer(product);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    private Object produce() {
        Object temp = Math.random();
        System.out.println("生产元素"+temp);
        return temp;
    }
}
class Consumer implements  Runnable{
    private   BlockingQueue queue;
    Consumer(BlockingQueue q){queue =q;}
    public  void run(){
            try {
                while(true){
                    Thread.sleep(1);
                    consume(queue.take());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         }
    void consume(Object x){
        System.out.println("线程:"+Thread.currentThread().getName()+"取走元素"+x);
    }
}

运行结果:

开始
生产元素0.034678478471825924
线程:消费者2取走元素0.034678478471825924
生产元素0.6500148045324159
线程:消费者1取走元素0.6500148045324159
生产元素0.5541330627553237
线程:消费者2取走元素0.5541330627553237
生产元素0.10212933192009888
线程:消费者1取走元素0.10212933192009888
生产元素0.6087282717897202
线程:消费者1取走元素0.6087282717897202
生产元素0.13872695661967127
线程:消费者1取走元素0.13872695661967127
生产元素0.7698183070089443
线程:消费者2取走元素0.7698183070089443
生产元素0.25240217812911747
线程:消费者1取走元素0.25240217812911747
......

上面这是因为我们生产者少于消费者   读者可以按照提供的代码将生产者多一些 会得到不同的输出

 

 

 

 

最后  源码注释里有一个before-happens的东西 如下,笔者姑且暂时解释如下:

Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
* {@code BlockingQueue}
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* actions subsequent to the access or removal of that element from
* the {@code BlockingQueue} in another thread.

内存一致性:和其他并发集合一样,在一个线程中,在将一个对象放入BlockingQueue的动作发生在在另一个线程中从 BlockingQueue 中访问或移除该元素之后的动作之前,这意味者更改无效。

 

happens-before   也就是说我们的读的操作要发生在写之后  这样对到的才是对的   否则  你刚刚读了  别的线程给你改了  这就尴尬了。。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值