可阻塞队列的实现

描述

队列包含固定长度的队列和不固定长度的队列。那什么是可阻塞队列呢?阻塞队列的作用和实际应用在哪里呢?阻塞队列又是如何实现的呢?

带着这几个问题我们就来详细了解一下阻塞队列吧!

想要用队列来实现可阻塞的队列,我们首先应该了解一下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通常则是由同一方设置和释放信号量。

原文地址:https://www.cnblogs.com/shen-smile/p/5146589.html