阻塞队列:应用于多线程环境,
当队列为空时,线程从中取元素会被阻塞并且置于等待状态,直到有线程向其中放入元素;
当队列为满时,向其中放入元素的线程会被阻塞并且置于等待状态,直到有线程取出元素。

ArrayBlockingQueue提供一种有界阻塞队列
PriorityBlockingQueue支持在并发情况下的优先级队列
LinkedBlockingQueue是无界的阻塞队列
LinkedBlockingDeque类提供对双端结点的操作
SynchronousQueue为异步队列,不存储元素
DelayQueue提供一种延时执行任务的队列

以一个生产者消费者的例子体现阻塞队列如何使用:

  1. import java.util.concurrent.ArrayBlockingQueue;
  2. import java.util.concurrent.BlockingQueue;
  3. public class TestBlockingQueue {
  4. static BlockingQueue<Person> blockingQueue = new ArrayBlockingQueue<>(10);
  5. public static void main(String[] args) {
  6. new Thread(() -> {
  7. for (int i = 1; i < 100; i++) {
  8. try {
  9. blockingQueue.put(new Person(i));
  10. System.out.println("第"+i+"个大佬进入队列排队");
  11. Thread.sleep(10);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. },"Producer").start();
  17. new Thread(() -> {
  18. for (int i = 1; i < 100; i++) {
  19. Person person = null;
  20. try {
  21. person = blockingQueue.take();
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. System.out.println(Thread.currentThread().getName()+ "-----将第"+person.id+"位大佬带出队列");
  26. }
  27. },"Consumer1").start();
  28. new Thread(() -> {
  29. for (int i = 0; i < 100; i++) {
  30. Person person = null;
  31. try {
  32. person = blockingQueue.take();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. System.out.println(Thread.currentThread().getName()+ "-----将第"+person.id+"位大佬带出队列");
  37. }
  38. },"Consumer2").start();
  39. new Thread(() -> {
  40. for (int i = 0; i < 100; i++) {
  41. Person person = null;
  42. try {
  43. person = blockingQueue.take();
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. System.out.println(Thread.currentThread().getName()+ "-----将第"+person.id+"位大佬带出队列");
  48. }
  49. },"Consumer3").start();
  50. }
  51. }
  52. class Person{
  53. int id;
  54. public Person(int id){
  55. this.id = id;
  56. }
  57. }

1. 阻塞队列的种类

1.1. ArrayBlockingQueue:

  • ArrayBlockingQueue是一个用数组实现的有界阻塞队列。
  • 此队列按照先进先出(FIFO)的原则对元素进行排序。
  • 默认不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。
  • 公平性是使用可重入锁实现的。

1.2. LinkedBlockingQueue:

LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。

1.3. PriorityBlockingQueue:

一个支持优先级的无界队列。默认情况下元素采取自然顺序排列,也可以通过比较器comparator来指定元素的排序规则。元素按照升序排列。

1.4. DelayQueue

是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。我们可以将DelayQueue运用在以下应用场景:

  • 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
  • 定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。

队列中的Delayed必须实现compareTo来指定元素的顺序。比如让延时时间最长的放在队列的末尾。

1.5. SynchronousBlockingQueue

是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue 和 ArrayBlockingQueue。

1.6. LinkedTransferQueue

LinkedTransferQueue是一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。

  • transfer方法。如果当前有消费者正在等待接收元素(消费者使用take()方法或带时间限制的poll()方法时),transfer方法可以把生产者传入的元素立刻transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
  • tryTransfer方法。则是用来试探下生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回false。和transfer方法的区别是tryTransfer方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。

参考文献

https://www.cnblogs.com/renjiaqi/p/11351570.html