java阻塞队列

BlockingQueue主要方法

java阻塞队列应用于生产者消费者模式、消息传递、并行任务执行和相关并发设计的大多数常见使用上下文。
       BlockingQueue在Queue接口基础上提供了额外的两种类型的操作,分别是获取元素时等待队列变为非空和添加元素时等待空间变为可用。
       BlockingQueue新增操作的四种形式:

add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常
offer       添加一个元素并返回true       如果队列已满,则返回false
poll         移除并返问队列头部的元素    如果队列为空,则返回null
peek       返回队列头部的元素             如果队列为空,则返回null
put         添加一个元素                      如果队列满,则阻塞
take        移除并返回队列头部的元素     如果队列为空,则阻塞

offer(E o, long timeout, TimeUnit unit);  //将指定的元素插入此队列中,如果没有可用空间,将等待指定的等待时间(如果有必要)。 
poll(long timeout, TimeUnit unit);  //检索并移除此队列的头部,如果此队列中没有任何元素,则等待指定等待的时间(如果有必要)。

 上述方法中主要用到的是put()和take()方法,也只有这两个方法具有阻塞等待功能,另外BlockingQueue 不接受 null 元素。试图 add、put 或 offer 一个 null 元素时,某些实现会抛出 NullPointerException。null 被用作指示 poll 操作失败的警戒值。
       BlockingQueue 可以定义为限定容量的队列,它有一个 remainingCapacity容量值,超出此容量,便无法无阻塞地 put 额外的元素。也可以定义为没有容量限制的队列,没有容量约束的 BlockingQueue 总是报告 Integer.MAX_VALUE 的剩余容量。
       BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁定或其他形式的并发控制来自动达到它们的目的。

BlockingQueue成员详细介绍

LinkedBlockingQueue的容量是没有上限的(说的不准确,在不指定时容量为Integer.MAX_VALUE,不要然的话在put时怎么会受阻呢),但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。


ArrayBlockingQueue在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来 达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序。ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;


PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对 PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞 队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元 素要具有比较能力。


DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。

代码实例

package study;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueTest {  
    public static void main(String[] args) {  
        final BlockingQueue queue = new ArrayBlockingQueue(3);  
        for(int i=0;i<2;i++){  
            new Thread(){  
                public void run(){  
                    while(true){  
                        try {  
                        Thread.sleep((long)(Math.random()*1000));  
                        System.out.println(Thread.currentThread().getName() + "准备放数据!");                              
                            queue.put(1);  
                            System.out.println(Thread.currentThread().getName() + "已经放了数据," +   "队列目前有" + queue.size() + "个数据");  
                        } catch (InterruptedException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
                  
            }.start();  
        }  
        new Thread(){  
            public void run(){  
                while(true){  
                    try {  
                    //将此处的睡眠时间分别改为100和1000,观察运行结果  
                    Thread.sleep(1000);  
                    System.out.println(Thread.currentThread().getName() + "准备取数据!");  
                    queue.take();  
                    System.out.println(Thread.currentThread().getName() + "已经取走数据," +   
                                "队列目前有" + queue.size() + "个数据");          
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }.start();            
    }  
}

使用阻塞队列两个显著的好处就是:多线程操作共同的队列时不需要额外的同步,另外就是队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。当许多线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。

你可能感兴趣的:(java,阻塞队列)