BlockingQueue阻塞队列
1.BlockingQueue介绍1.1.引言:1.2.阻塞队列介绍:1.3.阻塞队列的用处:1.4.接口架构图2.BlockingQueue核心方法3.BlockingQueue案例测试3.1.抛出异常的方法代码验证:3.2.返回特殊值的方法(boolean值返回)3.3.阻塞队列的方法3.4.超时中断的方法1.BlockingQueue介绍
1.1.引言:
阻塞虽然是不好的,但是我们有时候不得不阻塞,比如说一家餐厅人满了,店主当然不希望即将过来的这些客人全部都走,而是希望它们留下来继续排队等候。对于阻塞的内容,线程和资源的调度管理能力,这样就出了一个接口,就叫阻塞队列。在不得不阻塞,必须要阻塞的时候,如何管理好被阻塞的资源和线程,这就是阻塞队列。
队列的特性:先进先出,插入元素在队尾,删除元素在队头。
1.2.阻塞队列介绍:
阻塞:必须要阻塞/不得不阻塞
阻塞队列是一个队列,在数据结构中中起的作用如下图:
线程1往阻塞队列里添加元素,线程2从阻塞队列里移除元素。
阻塞队列,我们可以简单的理解为海底捞火锅店的候客区,如图绿框框就是我们的阻塞队列,即我们所理解的海底捞火锅店的候客区,里面等着的就是一桌一桌的食客。如果候客区也满了,还加不加人?
当队列是空的,从队列中获取元素的操作将会被阻塞。 当队列是满的,从队列中添加元素的操作将会被阻塞。 试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素。
对于上面阻塞的问题,我们又可以举一个例子,假如说Thread1是做蛋糕的师傅,Thread2是买蛋糕的顾客,而BlockingQueue是食品柜。当顾客来了,发现食品柜是空的,想消费蛋糕的操作将会被阻塞。如果蛋糕买的人少了,食品柜中的蛋糕满了,此时生产蛋糕的操作将会被阻塞。
**试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素。**简单的说,就是空的时候不能消费,满的时候不能增加。
试图向已满的队列中添加新元素的线程将会被阻塞,直到其它线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增。
1.3.阻塞队列的用处:
在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤起。
wait就是一种阻塞,notify是一种唤醒,await是一种阻塞, signal是唤醒。
为什么需要 BlockingQueue?
好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue 都给你一手包办了。在 concurrent 包发布以前,在多线程环境下,我们每个程序员都必须自己去控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
也就是不用手写wait和notify了。只要柜子里蛋糕满了,生产线程就会阻塞,只要柜子里没有蛋糕了,消费线程就会被阻塞。
1.4.接口架构图
BlockingQueue所有已知实现类:
ArrayBlockingQueue ,DelayQueue ,LinkedBlockingDeque, LinkedBlockingQueue , LinkedTransferQueue , PriorityBlockingQueue , SynchronousQueue
在我们以前学习集合的过程中,Collection接口有两个常用的接口List接口和Set接口,现在又有了阻塞队列接口BlockingQueue
ArrayBlockingQueue:由数组结构组成的有界阻塞队列
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列
PriorityBlockingQueue:支持优先级排序的无界阻塞队列
DelayQueue:使用优先级队列实现的延迟无界阻塞队列
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue:由链表结构组成的无界阻塞队列
LinkedBlockingDeque:由链表结构组成的双向阻塞队列
2.BlockingQueue核心方法
核心方法如下:
共同点:插入成功或者删除成功都返回true(除了put和take),区别在于处理异常情况
(1)抛出异常的方法 当阻塞队列满时,再往队列里add元素会抛出IllegalStateException:Queue full; 当阻塞队列空时,再从队列里remove移除元素会抛NoSuchElementException。
(2)返回特殊值(boolean值)的方法 插入成功,返回true;插入失败,返回false; 删除成功返回出队列元素;删除失败返回null;
(3)阻塞的方法(添加无返回值) 当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出。 当阻塞队列空时,消费者线程试图take队列里的元素,队列会一直阻塞消费者线程直到队列有可用元素。
(4)超时的方法 当向阻塞队列offer元素时候,时间超过了设定的值,就会出现超时中断; 当向阻塞队列poll元素时候,时间超过了设定的值,就会出现超时中断。
3.BlockingQueue案例测试
3.1.抛出异常的方法代码验证:
add方法抛出异常public static void main(String[] args) {//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.add("a"));System.out.println(blockingQueue.add("b"));System.out.println(blockingQueue.add("c"));//System.out.println(blockingQueue.add("d"));}
public static void main(String[] args) {//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.add("a"));System.out.println(blockingQueue.add("b"));System.out.println(blockingQueue.add("c"));System.out.println(blockingQueue.add("d"));}
当阻塞队列满时,再往队列里add元素会抛出IllegalStateException:Queue full;
remove方法抛出异常
public static void main(String[] args) {//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.add("a"));System.out.println(blockingQueue.add("b"));System.out.println(blockingQueue.add("c"));//remove会返回被移除的对象System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());}
当阻塞队列空时,再从队列里remove移除元素会抛NoSuchElementException。
element检查方法:(底层调用了 peek()方法)
public static void main(String[] args) {//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.add("a"));System.out.println(blockingQueue.add("a"));//element就是查看一下队首元素的值System.out.println(blockingQueue.element());}
3.2.返回特殊值的方法(boolean值返回)
阻塞队列offer方法的使用public static void main(String[] args) {BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.offer("a"));System.out.println(blockingQueue.offer("b"));System.out.println(blockingQueue.offer("c"));System.out.println(blockingQueue.offer("d"));}
插入成功,返回true;插入失败,返回false;
阻塞队列poll方法的使用
public static void main(String[] args) {BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.offer("a"));System.out.println(blockingQueue.offer("b"));System.out.println(blockingQueue.offer("c"));System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());}
删除成功返回出队列元素;删除失败返回null;
3.3.阻塞队列的方法
阻塞队列put方法使用public static void main(String[] args) throws InterruptedException {BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);blockingQueue.put("a");blockingQueue.put("a");blockingQueue.put("a");blockingQueue.put("a");}
没有报错,当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出。,即腾出空位置为止,能塞进去。与semaphore争车位类似
阻塞队列take方法使用
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);blockingQueue.put("a");blockingQueue.put("a");blockingQueue.put("a");System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());}
当阻塞队列空时,消费者线程试图take队列里的元素,队列会一直阻塞消费者线程直到队列有可用元素。
3.4.超时中断的方法
阻塞队列offer超时中断演示public static void main(String[] args) throws InterruptedException {BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));}
程序执行结果如下:当往阻塞队列添加第六个元素的时候,队列添加不进去,3秒后会超时中断。
当向阻塞队列offer元素时候,时间超过了设定的值,就会出现超时中断;
阻塞队列poll超时中断演示
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));}
当向阻塞队列poll元素时候,时间超过了设定的值,就会出现超时中断。