什么情况下我们会使用 阻塞队列:多线程并发处理,线程池的管理!
队列的阻塞和家族介绍
队列的阻塞
队列遵循先进先出(FIFO)的原则。
对应First Input First Ouput
首先是要讲的阻塞队列:
对应的API文档:介绍了如下的实现类
父接口Queue<E>
家族的成员
家族的成员:树形图
所以说:BlockingQueue 不是新的东西,只是你没有接触到,它和List、Set容器的实现类都是同级的关系。
学会使用BlockingQueue队列
实现类:ArrayBlockingQueue 非阻塞队列
需要掌握的就是:队列的添加、移除操作。
添加和移除的操作对应了4组API(4组添加和移除的操作,会产生以下的效果):
1、会抛出异常
3、阻塞、等待
4、超时等待
方法 | 抛出异常 | 不抛出异常,有返回值 | 阻塞、等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer(E e, long timeout, TimeUnit unit) |
移除 | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
检查队首元素 | element() | peek() | - | - |
package com.zxh.blockingQueue; import java.util.concurrent.ArrayBlockingQueue; public class Test { public static void main(String[] args) { test1(); } /** * 抛出异常 */ public static void test1(){ // 初始化队列的大小为3 ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); // 添加方法,添加成功返回true System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); System.out.println(blockingQueue.element()); // 检查队首元素 // java.lang.IllegalStateException: Queue full:非法状态异常:队列已满 // System.out.println(blockingQueue.add("d")); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); // java.util.NoSuchElementException:没有可删除的值,抛出异常 // System.out.println(blockingQueue.remove()); } }
队列已满的情况下添加:
没有值的情况下删除:
package com.zxh.blockingQueue; import java.util.concurrent.ArrayBlockingQueue; public class Test { public static void main(String[] args) { test2(); } /** * 不抛出异常,有返回值 */ public static void test2(){ ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); // 添加方法,添加成功返回true System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); System.out.println(blockingQueue.peek()); // 检查队首元素 // System.out.println(blockingQueue.offer("d")); // false,队列已满,添加失败返回false System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); // System.out.println(blockingQueue.poll()); // 队列为空,删除失败返回 null } }
队列已满的情况下添加:
没有值的情况下删除:
package com.zxh.blockingQueue; import java.util.concurrent.ArrayBlockingQueue; public class Test { public static void main(String[] args) throws InterruptedException { test3(); } /** * 阻塞、等待 */ public static void test3() throws InterruptedException { ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3); // 添加方法 blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c"); // blockingQueue.put("d"); //如果队列已满,再次添加会一直等待有元素移除,就会造成阻塞 // 移除方法 System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); // System.out.println(blockingQueue.take()); //如果队列为空,再次移除会一直等待新的元素加入,就会造成阻塞 } }
队列已满的情况下添加:
没有值的情况下删除:
package com.zxh.blockingQueue; import java.util.concurrent.ArrayBlockingQueue; public class Test { public static void main(String[] args) throws InterruptedException { test4(); } /** * 超时等待 */ public static void test4() throws InterruptedException { ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3); // 添加方法 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); // offer(E e, long timeout, TimeUnit unit):e表示要添加的值,timeout等待时间,unit延迟类型 // 如果队列已满,等待2s后,终止程序,返回false // System.out.println(blockingQueue.offer("d", 2, TimeUnit.SECONDS)); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); // poll(long timeout, TimeUnit unit):timeout等待时间, unit延迟类型 // 如果队里为空,再次删除会等待2s,终止程序,返回null // System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS)); } }
队列已满的情况下添加:等待2s终止程序,返回false
没有值的情况下删除:会等待2s,终止程序,返回null
实现类:SynchronousQueue 同步队列
同步队列SynchronousQueue
-
1、不存储元素
-
2、同步操作,存储一个元素后,必须要拿出来,才能再往里面存储
-
3、当存储一个元素后,再存储元素,就会阻塞,因为需要等待元素的取出
测试同时存储2个元素的情况
public class SynchronousQueueTest { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); try { blockingQueue.put("A"); blockingQueue.put("B"); // 无法再次存储,队列会阻塞,同步队列只能存再取 } catch (InterruptedException e) { e.printStackTrace(); } } }
就会一直等待取出
测试多个线程存取
- 结果:每一次存储后,都会等待取出之后,才会再次存储。
package com.zxh.blockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; /** * 同步队列SynchronousQueue * 1、不存储元素 * 2、同步操作,存储一个元素后,必须要拿出来,才能再往里面存储 * 3、当存储一个元素后,再存储元素,就会阻塞,因为需要等待元素的取出 * */ public class SynchronousQueueTest { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName() + "=> put 1"); blockingQueue.put("1"); System.out.println(Thread.currentThread().getName() + "=> put 2"); blockingQueue.put("2"); System.out.println(Thread.currentThread().getName() + "=> put 3"); blockingQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } }, "T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "=> get 1"); blockingQueue.take(); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "=> get 2"); blockingQueue.take(); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "=> get 3"); blockingQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } }, "T2").start(); } }
先存再取,同步操作