BlockingQueue作为线程容器,可以为线程同步提供有力的保障,其主要用到的方法包括:
add(E o); //将指定的元素添加到此队列中(如果立即可行),在成功时返回 true,其他情况则抛出 IllegalStateException。 drainTo(Collection<? super E> c); //移除此队列中所有可用的元素,并将它们添加到给定 collection 中。 drainTo(Collection<? super E> c,int maxElements);//最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中 offer(E o); //如果可能的话,将指定元素插入此队列中。 offer(E o, long timeout, TimeUnit unit); //将指定的元素插入此队列中,如果没有可用空间,将等待指定的等待时间(如果有必要)。 poll(long timeout, TimeUnit unit); //检索并移除此队列的头部,如果此队列中没有任何元素,则等待指定等待的时间(如果有必要)。 put(E o); //将指定元素添加到此队列中,如果没有可用空间,将一直等待(如果有必要)。 remainingCapacity(); //返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的元素数量;如果没有内部限制,则返回 Integer.MAX_VALUE。 take(); //检索并移除此队列的头部,如果此队列不存在任何元素,则一直等待。
BlockingQueue 可以定义为限定容量的队列,它有一个 remainingCapacity容量值,超出此容量,便无法无阻塞地 put 额外的元素。也可以定义为没有容量限制的队列,没有容量约束的 BlockingQueue 总是报告 Integer.MAX_VALUE 的剩余容量。
BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁定或其他形式的并发控制来自动达到它们的目的。然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)没有 必要自动执行,除非在实现中特别说明。因此,举例来说,在只添加了 c 中的一些元素后,addAll(c) 有可能失败(抛出一个异常)。它实质上不 支持使用任何一种“close”或“shutdown”操作来指示不再添加任何项。这种功能的需求和使用有依赖于实现的倾向。例如,一种常用的策略是:对于生产者,插入特殊的 end-of-stream 或 poison 对象,并根据使用者获取这些对象的时间来对它们进行解释。
BlockingQueue 主要用于实现生产者-使用者队列,但它另外还支持 Collection 接口。因此,举例来说,使用 remove(x) 从队列中移除任意一个元素是有可能的。然而,这种操作通常不 会有效执行,只能有计划地偶尔使用,比如在取消排队信息时。阻塞队列与Semaphore有很大相似性,但也有很多不同,阻塞队列一般是一方存数据,另一方释放数据,而Semaphore通常是同一方获取和释放信号。下面通过一个例子加以说明:
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(); } }
Thread-1准备放数据! Thread-1已经放了数据,队列目前有1个数据 Thread-1准备放数据! Thread-1已经放了数据,队列目前有2个数据 Thread-0准备放数据! Thread-0已经放了数据,队列目前有3个数据 Thread-0准备放数据! Thread-2准备取数据! Thread-2已经取走数据,队列目前有2个数据
BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1); BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1); public void sub(int i){ try { queue1.put(1); } catch (InterruptedException e) { e.printStackTrace(); } for(int j=1;j<=10;j++){ System.out.println("sub thread sequece of " + j + ",loop of " + i); } try { queue2.take(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void main(int i){ try { queue2.put(1); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } for(int j=1;j<=100;j++){ System.out.println("main thread sequece of " + j + ",loop of " + i); } try { queue1.take(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }