缓冲队列 BlockingQueue

缓冲队列 BlockingQueue

一、BlockingQueue缓存队列简单概述

1、什么是阻塞队列BlockingQueue

BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种:

  1. 当队列满了的时候进行入队列操作
  2. 当队列空了的时候进行出队列操作

因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空队列进行出队列操作时,它将会被阻塞,除非有另一个线程进行了入队列操作。

阻塞队列主要用在生产者/消费者的场景,当BlockingQueue中的add方法去放入商品时,如果队列中的空间已满,则无法放入,会抛出异常;反之,当BlockingQueue中的remove方法去取出商品时,如果队列中的空间为空,则无法取出,也会抛出异常。

下面展示下面这幅图展示了一个线程生产、一个线程消费的场景:
在这里插入图片描述

2、BlockingQueue接口中的部分方法
接口方法方法简单介绍
add(E e)在不违反容量限制的情况下,可立即将指定元素插入此队列,成功返回true,当无可用空间时候,返回IllegalStateException异常。
offer(E e)在不违反容量限制的情况下,可立即将指定元素插入此队列,成功返回true,当无可用空间时候,返回false。
put(E e)直接在队列中插入元素,当无可用空间时候,阻塞等待。
offer(E e, long time, timeunit unit)将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false。
E take()获取并移除队列头部的元素,无元素时候阻塞等待。
E poll( long time, timeunit unit)获取并移除队列头部的元素,无元素时候阻塞等待指定时间。
3、BlockingQueue的父类与部分实现类

在这里插入图片描述
ArrayBlockingQueue和SynchronousQueue都是BlockingQueue的实现类。下面主要以这两个类为例子进一步解析阻塞队列。

二、ArrayBlockingQueue类

1、什么是ArrayBlockingQueue

ArrayBlockingQueue是一个有限的blocking queue由数组支持。 这个队列排列元素FIFO(先进先出)。 队列的头部是队列中最长的元素。 队列的尾部是队列中最短时间的元素。 新元素插入队列的尾部,队列检索操作获取队列头部的元素。

这是一个经典的“有界缓冲区”,其中固定大小的数组保存由生产者插入的元素并由消费者提取。 创建后,容量无法更改。使用put方法将新元素放入满队列时会导致线程阻塞,使用take方法从空队列中取元素时也会导致线程阻塞。

此类支持可选的公平策略,用于订购等待的生产者和消费者线程。 默认情况下,是非公平的。即不保证等待时间最长的队列最优先能够访问队列。

2、ArrayBlockingQueue中的四组API
抛出异常不抛出异常,有返回值阻塞等待超时等待
添加元素方法add(E e)offer(E e)put(E e)offer(E e, long timeout, TimeUnit unit)
移除元素方法remove(Object o)poll()take()poll(long timeout, TimeUnit unit)
检测队首元素element()peek()--
3、代码实现

抛出异常

public class BlockingQueueTest {
    public static void main(String[] args) {
        test01();
    }

    //BlockingQueue阻塞队列会抛出异常
    public static void test01() {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        //获取首元素 java.util.NoSuchElementException
        //blockingQueue.element();

        blockingQueue.add("aa");
        blockingQueue.add("bb");
        blockingQueue.add("cc");
        //会抛出 java.lang.IllegalStateException: Queue full 异常
        //blockingQueue.add("dd");
        System.out.println("===========================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //会抛出 java.util.NoSuchElementException 异常
        //System.out.println(blockingQueue.remove());
    } 
}

不抛出异常,有返回值

public class BlockingQueueTest {
    public static void main(String[] args) {
        test02();
    }

    //BlockingQueue阻塞队列不会抛出异常,有返回值
    public static void test02() {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        //获取首元素
        //System.out.println(blockingQueue.peek()); 返回 null

        System.out.println(blockingQueue.offer("aa"));
        System.out.println(blockingQueue.offer("bb"));
        System.out.println(blockingQueue.offer("cc"));
        //System.out.println(blockingQueue.offer("dd")); 返回 false
        System.out.println("===========================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //System.out.println(blockingQueue.poll()); 返回 null
    }
}

阻塞等待

public class BlockingQueueTest {
    public static void main(String[] args) {
        test03();
    }

    //BlockingQueue阻塞队列会阻塞等待
    public static void test03() {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        try {
            blockingQueue.put("aa");
            blockingQueue.put("bb");
            blockingQueue.put("cc");
            //会一直等待直到队列内有空间可以放入数据
            //blockingQueue.put("dd");
		   System.out.println("===========================");
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            //会一直等待直到队列内有数据
            //System.out.println(blockingQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

超时等待

public class BlockingQueueTest {
    public static void main(String[] args) {
        test04();
    }
    //BlockingQueue阻塞队列会超时等待
    public static void test04() {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        try {
            blockingQueue.offer("aa");
            blockingQueue.offer("bb");
            blockingQueue.offer("cc");
            //会等待两秒,如果队列一直没有空间,就退出
            //blockingQueue.offer("dd",1L, TimeUnit.SECONDS);
            System.out.println("===================");
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());
            //会等待两秒,如果队列一直为空,就退出
            System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

三、SynchronousQueue类

1、什么是SynchronousQueue(同步队列)
1. A blocking queue其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量, 甚至没有一个容量。
2. 你不能peek在同步队列,因为一个元素,当您尝试删除它才存在; 您无法插入元素(使用任何方法), 除非另有线程正在尝试删除它;
3. 你不能迭代,因为没有什么可以迭代。
4. 队列的头部是第一个排队的插入线程尝试添加到队列中的元素; 如果没有这样排队的线程,那么没有元素可用于删除,并且poll()将返回null 。
5. 为了其他Collection方法(例如contains )的目的,SynchronousQueue充当空集合。 此队列不允许null元素。
2、使用代码去解释上述的内容

SynchronousQueue(同步队列)的插入操作必须对应一个线程中的删除操作,如果只在单线程中去插入元素,则会导致当前的线程陷入阻塞等待,反之相同。

简单图解:
在这里插入图片描述

代码实现:

public class SynchronousQueueTest02 {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + "放入一个元素");
                blockingQueue.put("宝马汽车");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "取出元素:" + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
/*
	输出结果:
	Thread-0放入一个元素
	Thread-1取出元素:宝马汽车
*/

执行程序时,Thread-0线程抢占到了cup资源,在控制台输出”Thread-0放入一个元素“,并执行blockingQueue.put(“宝马汽车”)代码,由于此时在Thread-1线程中还未执行blockingQueue.take()代码,所以Thread-0线程陷入阻塞;当Thread-1线程执行到 blockingQueue.take()代码时会唤醒Thread-0线程继续执行,Thread-0线程往队列中放入元素,此时Thread-1线程就能移除队列中的元素。反之相同。

不能在SynchronousQueue(同步队列)中使用peek()方法,因为SynchronousQueue内部不像ArrayBlockingQueue或LinkedListBlockingQueue,它并没有数据缓存空间,不能调用peek()方法来看队列中是否有数据元素,因为数据元素只有尝试取出的时候才可能会存在,不取走时只想偷窥一下是不行的。

使用peek()方法返回的永远是null。

队列头元素是第一个排队要插入数据的线程,而不是要交换的数据。数据是在配对的生产者和消费者线程之间直接传递的,并不会将数据缓冲数据到队列中。可以这样来理解:生产者和消费者互相等待对方,握手,然后一起离开。

代码实现:

public class SynchronousQueueTest02 {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + "放入一个元素");
                blockingQueue.put("宝马汽车");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "取出元素:" + blockingQueue.peek());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
/*
	输出结果:
	Thread-0放入一个元素
	Thread-1取出元素:null
	程序会一直运行,因为Thread-0生产者线程并没有匹配到一个消费者线程去取数据元素,所以会陷入阻塞状态。
*/

SynchronousQueue(同步队列)不能够去迭代,因为他的内部没有容量。

如果在SynchronousQueue(同步队列)中没有一个尝试去插入数据元素的线程,那么poll()将返回null 。如上面提到的poll()方法不抛出异常,有返回值。

代码实现:

public class SynchronousQueueTest02 {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "取出元素:" + blockingQueue.poll());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
/*
	输出结果:
	Thread-0取出元素:null
*/

对于其他 Collection 方法(例如 contains),SynchronousQueue 作为一个空集合。此队列不允许 null 元素。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值