描述
队列包含固定长度的队列和不固定长度的队列。那什么是可阻塞队列呢?阻塞队列的作用和实际应用在哪里呢?阻塞队列又是如何实现的呢?
带着这几个问题我们就来详细了解一下阻塞队列吧!
想要用队列来实现可阻塞的队列,我们首先应该了解一下BlockingQueue这个接口。查看BlockingQueue帮助文档发现,有各个方法的区别对比的表格(见下表)。发现只有put和take方法才具有阻塞效果。
Throws exception | Special value | Blocks | Times out | |
Insert | add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
Remove | remove() |
poll() |
take() |
poll(time, unit) |
Examine | element() |
peek() |
not applicable | not applicable |
可阻塞的队列我想应该不用多解释了,阻塞队列可以应用于线程之间的切换运行,也可以用于数据的共享与同步等。阻塞队列的实现方法在下面的Example中介绍的很详细,可以将代码实际运行之后分析结果来学习。
Example
下面我将使用 具有3个空间的队列来演示阻塞队列的功能和效果。
示例代码:
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(); } }
接下来我将使用 两个具有1个空间的队列来实现同步通知的功能。
我们先创建两个具有1个空间的队列queue1和queue2,在匿名构造方法中为queue2先执行put()方法放入一个数据。此时执行到queue2的put()方法会被阻塞,而此时queue1可以执行put()方法,put()方法完成后调用queue2的take()方法取走数据,所以queue2的put()方法就可以执行了,queue2执行完成后,再调用queue1的take()方法。如此就能实现我们的要求了!
示例代码:
public class BlockingQueueCommunication { public static void main(String[] args) { final Business business = new Business(); new Thread( new Runnable(){ public void run(){ for(int i=1;i<=50;i++){ business.sub(i); } } } ).start(); for(int i=1;i<=50;i++){ business.main(i); } } static class Business { BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1); BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1); //匿名构造方法,只会在创建类的实例时执行一次 { try { queue2.put(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void sub(int i){ try { queue1.put(1); } catch (InterruptedException e) { // TODO Auto-generated catch block 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 e) { // TODO Auto-generated catch block e.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(); } } } }
阻塞队列与Semaphore有些相似,但也不同。阻塞队列是一方存放数据,另一方释放数据,Semaphore通常则是由同一方设置和释放信号量。