简介:
Java中的BlockingQueue是java.util.concurrent包中的一个接口,是JDK中的并发工具,提供了线程安全的队列,可以用来协调生产者与消费者线程的生产和消费的速度,并且解决了高并发下数据读写的安全问题。BlockingQueue具有阻塞的复杂行为,可以实现生产、消费线程集合的同步。
Java中有两个BlockingQueue的实现类ArrayBlockingQueue和LinkedBlockingQueue,两者都实现了BlockingQueue接口,但是在实现细节上有所不同,下面将对它们进行详细讲解。
一、ArrayBlockingQueue
ArrayBlockingQueue是一个有界阻塞队列,它的容量固定,在创建时必须指定容量大小,并且不可更改。它内部以数组进行实现,因此其读写速度较快。
1.1 初始化
ArrayBlockingQueue的初始化需要指定容量大小,如下所示:
BlockingQueue<String> queue = new ArrayBlockingQueue<>(capacity);
其中capacity表示队列的容量大小。
1.2 队列操作
ArrayBlockingQueue提供了一系列的队列操作方法,例如:
- add(E e) 将指定元素插入此队列。
- put(E e) 将指定元素插入此队列,如有必要阻塞线程等待可用。
- take() 移除并返回此队列的头部,如果此队列为空,则阻塞线程等待可用。
- poll() 检索并移除此队列的头,如果此队列为空,则返回 null。
示例:
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
queue.add("A");
queue.add("B");
queue.add("C");// 添加第三个元素,会抛出IllegalStateException异常
运行结果:
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
从上面的运行结果中可以看出,当添加元素达到队列容量时,再次添加元素会抛出IllegalStateException异常。
示例:
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
queue.put("A");
queue.put("B");
queue.put("C");// 阻塞线程,直到队列中有空闲位置
String element = queue.take();// 从队列中取出元素,如果队列为空则阻塞线程,直到有可用元素
1.3 等待超时操作
ArrayBlockingQueue还提供了等待超时操作的方法,例如:
- offer(E e, long timeout, TimeUnit unit) 将指定元素插入此队列,并在指定的等待时间内等待空间变得可用。
- poll(long timeout, TimeUnit unit) 检索并移除此队列的头,如果此队列为空,则在指定的等待时间内等待可用元素。
示例:
BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
queue.offer("A", 1000, TimeUnit.MILLISECONDS);
queue.offer("B", 1000, TimeUnit.MILLISECONDS);
queue.offer("C", 1000, TimeUnit.MILLISECONDS);// 阻塞线程,直到队列中有空闲位置,等待1秒
String element = queue.poll(1000, TimeUnit.MILLISECONDS);// 从队列中取出元素,如果队列为空则等待1秒,直到有可用元素
二、LinkedBlockingQueue
LinkedBlockingQueue是一个无界阻塞队列,它的容量没有限制,在创建时可以选择是否指定容量大小。它内部以链表进行实现,因此其读写速度较慢,但是其容量不受限制,可以用于解决任务生产速度与消费速度不一致的场景。
2.1 初始化
LinkedBlockingQueue的初始化可以选择是否指定容量大小,如下所示:
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
或者指定容量大小,例如:
BlockingQueue<String> queue = new LinkedBlockingQueue<>(capacity);
其中capacity表示队列的容量大小,如果不指定则默认为Integer.MAX_VALUE。
2.2 队列操作
LinkedBlockingQueue也提供了一系列的队列操作方法,与ArrayBlockingQueue的方法类似,例如:
- add(E e) 将指定元素插入此队列。
- put(E e) 将指定元素插入此队列,如有必要阻塞线程等待可用。
- take() 移除并返回此队列的头部,如果此队列为空,则阻塞线程等待可用。
- poll() 检索并移除此队列的头,如果此队列为空,则返回 null。
示例:
BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
queue.add("A");
queue.add("B");
queue.add("C");// 添加第三个元素,不会抛出异常
String element = queue.poll();// 从队列中取出元素,如果队列为空则返回null
示例:
BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
queue.put("A");
queue.put("B");
queue.put("C");// 阻塞线程,直到队列中有空闲位置
String element = queue.take();// 从队列中取出元素,如果队列为空则阻塞线程,直到有可用元素
2.3 等待超时操作
LinkedBlockingQueue也提供了等待超时操作的方法,例如:
- offer(E e, long timeout, TimeUnit unit) 将指定元素插入此队列,并在指定的等待时间内等待空间变得可用。
- poll(long timeout, TimeUnit unit) 检索并移除此队列的头,如果此队列为空,则在指定的等待时间内等待可用元素。
示例:
BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);
queue.offer("A", 1000, TimeUnit.MILLISECONDS);
queue.offer("B", 1000, TimeUnit.MILLISECONDS);
queue.offer("C", 1000, TimeUnit.MILLISECONDS);// 阻塞线程,直到队列中有空闲位置,等待1秒
String element = queue.poll(1000, TimeUnit.MILLISECONDS);// 从队列中取出元素,如果队列为空则等待1秒,直到有可用元素
总结:
ArrayBlockingQueue和LinkedBlockingQueue都是BlockingQueue的实现类,都可以用来实现生产者和消费者模型,但是它们在实现方式上有所不同。ArrayBlockingQueue是一个有界阻塞队列,内部以数组进行实现,读写速度较快;LinkedBlockingQueue是一个无界阻塞队列,内部以链表进行实现,读写速度较慢。
当生产者的速度大于消费者的速度时,ArrayBlockingQueue可以避免队列无限增长的问题;当生产者和消费者速度相差不大,或者需要无限制地添加元素时,可以选择使用LinkedBlockingQueue。
参考文章:https://www.jianshu.com/p/97cccbb9fc99
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中ArrayBlockingQueue和LinkedBlockingQueue - Python技术站