学做网站论坛vip教程,网站怎么被百度收录,做视频网站的挣钱吗,如何创立网站今天我们来盘点一下Java中的Queue家族#xff0c;总共涉及到18种Queue。这篇恐怕是市面上最全最细讲解Queue的。本篇主要内容如下#xff1a;本篇主要内容帮你总结好的阻塞队列#xff1a;18种Queue总结一、Queue自我介绍 队列原理图1.1 Queue自我介绍hi#xff0c;大家好总共涉及到18种Queue。这篇恐怕是市面上最全最细讲解Queue的。本篇主要内容如下本篇主要内容帮你总结好的阻塞队列18种Queue总结一、Queue自我介绍 队列原理图1.1 Queue自我介绍hi大家好我的英文名叫Queue中文名叫队列无论现实生活中还是计算机的世界中我都是一个很重要的角色哦~我是一种数据结构大家可以把我想象成一个数组元素从我的一头进入、从另外一头出去称为FIFO原则先进先出原则。我还有两个亲兄弟List列表、Set集他们都是Collection的儿子我还有一个远房亲戚Map映射。他们都是java.util包这个大家庭的成员哦~1.2 现实生活中的场景海底捞排号等位先排号的优先进餐厅邮政员寄送信件信箱是队列1.3 计算机世界中的场景消息队列 RabbitMQUDP协议接收端将消息存放在队列中从队列中读取数据二、高屋建瓴纵览全局 18种队列分为三大类 接口、抽象类、普通类。弄清楚下面的继承实现关系对后面理解18种队列有很大帮助。18个Queue的继承实现关系图Queue接口继承 Collection接口Collection接口继承 Iterable接口BlockingQueue接口、Deque接口 继承 Queue接口AbstractQueue抽象类实现 Queue接口BlockingDeque接口、TransferQueue接口继承 BlockingQueue接口BlockingDeque接口继承Deque接口LinkedBlockingDeque类实现 BlockingDeque接口LinkedTransferQueue类接口实现 TransferQueue接口LinkedList类、ArrayDeque类、ConcurrentLinkedDeque类实现 了Deque接口ArrayBlockingQueue类、LinkendBlockingQueue类、LinkedBlockingDeque类、LinkedTransferQueue类、SynchronousQueue类、PriorityBlockQueue类、DelayQueue类继承 了AbstractQueue抽象类和实现了BlockingQueue接口PriorityQueue类和ConcurrentLinkedQueue类继承 了AbstractQueue抽象类注意Deque全称Double-Ended queue表示双端队列。类实现接口用implements接口继承接口用 extends类继承类用extends三、万物归宗Queue接口 2.1 深入理解Queue接口的本质Queue接口是一种Collection被设计用于处理之前临时保存在某处的元素。除了基本的Collection操作之外队列还提供了额外的插入、提取和检查操作。每一种操作都有两种形式如果操作失败则抛出一个异常如果操作失败则返回一个特殊值null或false取决于是什么操作。队列通常是以FIFO先进先出的方式排序元素但是这不是必须的。只有优先级队列可以根据提供的比较器对元素进行排序或者是采用正常的排序。无论怎么排序队列的头将通过调用remove()或poll()方法进行移除。在FIFO队列种所有新的元素被插入到队尾。其他种类的队列可能使用不同的布局来存放元素。每个Queue必须指定排序属性。2.2 Queue接口的核心方法总共有3组方法每一组方法对应两个方法。如下图所示Queue的核心方法动作抛异常返回特殊值Insertadd(e)offer(e)Removeremove()pollExamineelement()peek()1比如添加Insert元素的动作会有两种方式add(e)和offer(e)。如果调用add(e)方法时添加失败则会抛异常而如果调用的是offer(e)方法失败时则会返回false。offer方法用于异常是正常的情况下使用比如在有界队列中优先使用offer方法。假如队列满了不能添加元素offer方法返回false这样我们就知道是队列满了而不是去handle运行时抛出的异常。2同理移除Remove元素的动作队列为空时remove方法抛异常而poll返回null。如果移除头部的元素成功则返回移除的元素。3同理检测Examine元素的动作返回头部元素最开始加入的元素但不删除元素 如果队列为空则element()方法抛异常而peek()返回false。4Queue接口没有定义阻塞队列的方法这些方法在BlockQueue接口中定义了。5Queue实现类通常不允许插入null元素尽管一些实现类比如LinkedList不禁止插入null但是还是不建议插入null因为null也被用在poll方法的特殊返回值以说明队列不包含元素。四、双端可用Deque接口 4.1 深入理解Deque接口的原理双端队列Deque1Deque概念 支持两端元素插入和移除的线性集合。名称deque是双端队列的缩写通常发音为deck。大多数实现Deque的类对它们包含的元素的数量没有固定的限制的支持有界和无界。2Deque方法说明Deque方法说明该列表包含包含访问deque两端元素的方法提供了插入移除和检查元素的方法。这些方法种的每一种都存在两种形式如果操作失败则会抛出异常另一种方法返回一个特殊值null或false取决于具体操作。插入操作的后一种形式专门设计用于容量限制的Deque实现大多数实现中插入操作不能失败所以可以用插入操作的后一种形式。Deque接口扩展了Queue接口当使用deque作为队列时作为FIFO。元素将添加到deque的末尾并从头开始删除。作为FIFO时等价于Queue的方法如下表所示比如Queue的add方法和Deque的addLast方法等价。Deque也可以用作LIFO后进先出栈这个接口优于传统的Stack类。当作为栈使用时元素被push到deque队列的头而pop也是从队列的头pop出来。Stack栈的方法正好等同于Deque的如下方法注意peek方法不论是作为栈还是队列都是从队列的检测队列的头返回最先加入的元素。比如第一次put 100第二次put 200则peek返回的是100。如下图所示示例代码4.1 哪些类实现了Deque接口LinkedList类ArrayDeque类ConcurrentLinkedDeque类LinkedBlockingDeque类4.2 哪些类继承了Deque接口BlockingDeque接口五、队列骨架AbstractQueue抽象类 5.1 深入理解AbstractQueue抽象类AbstractQueue是一个抽象类继承了Queue接口提供了一些Queue操作的骨架实现。AbstractQueue的方法方法add、remove、element方法基于offer、poll和peek。也就是说如果不能正常操作则抛出异常。我们来看下AbstactQueue是怎么做到的。AbstractQueue的add方法public boolean add(E e) {if (offer(e))return true;elsethrow new IllegalStateException(Queue full);
}
AbstractQueue的remove方法public E remove() {E x poll();if (x ! null)return x;elsethrow new NoSuchElementException();
}
AbstractQueue的element方法public E element() {E x peek();if (x ! null)return x;elsethrow new NoSuchElementException();
}
注意如果继承AbstractQueue抽象类则必须保证offer方法不允许null值插入。5.2 哪些类继承了AbstractQueue抽象类ArrayBlockingQueue类、LinkendBlockingQueue类、LinkedBlockingDeque类、LinkedTransferQueue类、SynchronousQueue类、PriorityBlockQueue类、DelayQueue类继承 了AbstractQueue抽象类PriorityQueue类和ConcurrentLinkedQueue类继承 了AbstractQueue抽象类六、阻塞缓冲BlockingQueue接口 6.1 宏观来看BlockingQueue阻塞队列BlockQueue满了PUT操作被阻塞阻塞队列满了的情况BlockQueue为空Take操作被阻塞阻塞队列为空的情况1BlockingQueue阻塞队列也是一种队列支持阻塞的插入和移除方法。3阻塞的插入当队列满时队列会阻塞插入元素的线程直到队列不满。4阻塞的移除当队列为空获取元素的线程会等待队列变为非空。5应用场景生产者和消费者生产者线程向队列里添加元素消费者线程从队列里移除元素阻塞队列时获取和存放元素的容器。6为什么要用阻塞队列生产者生产和消费者消费的速率不一样需要用队列来解决速率差问题当队列满了或空的时候则需要阻塞生产或消费动作来解决队列满或空的问题。6.2 案例解析线程A往阻塞队列Blocking Queue中添加元素而线程B从阻塞队列中移除元素。当阻塞队列为空的时候 一个元素都没有则从队列中获取元素的操作将会被阻塞。生活中的案例去海底捞吃火锅的时候早上8点没人来吃火锅所以需要等客人过来。翻译成线程现在没有元素需要添加而且阻塞队列为空所以线程B不需要从队列中拿元素出来所以线程B获取元素的操作被阻塞了。当阻塞队列满了的时候 所有位置都放有元素则从队列中添加元素的操作将会被阻塞。生活中的案例去海底捞吃火锅的时候人太多了需要排号等其他桌空出来了才能进去。翻译成线程线程A往阻塞队列中添加元素将队列填满了线程B现在正在忙无法拿出队列中的元素所以阻塞队列没有地方再放元素了这个时候线程A添加元素的操作就被阻塞了6.3 操刀BlockingQueue接口BlockingQueue接口的10个核心方法继承的方法10个核心方法总结如下BlockingQueue接口的10个核心方法有三大类操作插入、移除、检查。插入有四种方法 add、offer、put、offer超时版。IllegalStateException - 队列满了ClassCastException - 添加的元素类型不匹配NullPointerException - 添加的元素为nullIllegalArgumentException - 添加的元素某些属性不匹配add方法特别之处用于添加失败时抛出异常共有四种异常offer方法特别之处用于添加失败时只返回falseput方法特别之处用于当阻塞队列满时生产者如果往队列里put元素则队列会一直阻塞生产者线程直到队列可用或者响应中断退出offer超时方法特别之处用于当阻塞队列满时生产者如果往队列里面插入元素队列会阻塞生产者线程一段时间如果超过了指定时间生产者线程会退出并返回false。移除有四种方法 remove、poll、take、poll超时版NoSuchElementException - 如果这个队列是空的remove方法特别之处用于移除失败时抛出异常poll方法特别之处用于移除失败时返回nulltake方法特别之处用于当阻塞队列为空时消费者线程如果从队列里面移除元素则队列会一直阻塞消费者线程直到队列不为空poll超时方法特别之处用于当阻塞队列空时消费者如果从队列里面删除元素则队列会一直阻塞消费者线程如果超过了指定时间消费者线程会退出并返回null检查有两种方法 element、peekelement方法用于检测头部元素的存在性如果队列为空则抛出异常否则返回头部元素。peek方法用于检测头部元素的存在性如果队列为空返回特殊值null否则返回头部的元素。6.4 BlockingQueue通过什么来阻塞插入和移除的当往队列里插入一个元素时如果队列不可用那么阻塞生产者主要通过LockSupport. parkthis来实现。park这个方法会阻塞当前线程只有以下4种情况中的一种发生时该方法才会返回。与park对应的unpark执行或已经执行时。“已经执行”是指unpark先执行然后再执行park的情况。线程被中断时。等待完time参数指定的毫秒数时。异常现象发生时这个异常现象没有任何原因。6.5 哪些类继承了BlockingQueue接口BlockingDeque接口 - 双端阻塞队列TransferQueue接口 - 传输队列6.6 哪些类实现了BlockingQueue接口ArrayBlockingQueue类 - 由数组构成的有界阻塞队列LinkedBlockingQueue类 - 由链表构成的有界阻塞队列界限默认大小为Integer.MAX_Value2^31-1值非常大相当于无界。LinkedBlockingDeque类 - 由链表构成的双向阻塞队列LinkedTransferQueue类 - 由链表构成的无界阻塞队列SynchronousQueue类 - 不存储元素的阻塞队列只有一个元素进行数据传递。LinkedTransferQueue类 - 由链表构成的无界阻塞TransferQueue队列DelayQueue类 - 使用优先级队列实现的延迟无界阻塞队列6.6 BlockingQueue接口继承了哪些接口BlockingQueue接口继承了Queue接口可作为队列使用七、双端阻塞BlockingDeque接口 7.1 从原理图上理解BlockDequeBlockQueue满了两端的Take操作被阻塞BlockingDeque满了BlockQueue为空两端的Take操作被阻塞BlockQueue为空7.2 BlockingDeque接口方法是阻塞队列BlockingQueue和双向队列Deque接口的结合。有如下方法BlockingDeque接口方法示例尝试执行以下方法LinkedBlockingDeque queue new LinkedBlockingDeque();
queue.addFirst(test1);
queue.addFirst(300);
queue.addLast(400);
最后队列中的元素顺序如下300, test1, 400。先添加了test1放到队列的头部然后在头部的前面放入300所以300在最前面成为头部然后将400放入队列的尾部所以最后的结果是300, test1, 400。队列种的元素7.3 BlockDeque和BlockQueue的对等方法BlockDeque和BlockQueue的对等方法7.4 BlockingDeque的特点线程安全。不允许使用null元素。无界和有界都可以。7.5 BlockingDeque接口继承了哪些接口Queue接口具有队列的功能Deque接口具有双端队列的功能BlockingQueue接口可作为阻塞队列使用7.6 哪些类实现了BlockDeque接口LinkedBlockingDeque八、使命必达TransferQueue接口 8.1 Transfer怎么理解如果有消费者正在获取元素则将队列中的元素传递给消费者。如果没有消费者则等待消费者消费。我把它称作使命必达队列必须将任务完成才能返回。8.2 生活中的案例针对TransferQueue的transfer方法圆通快递员要将小明的2个快递送货到门韵达快递员也想将小明的2个快递送货到门。小明一次只能拿一个快递员必须等小明拿了一个后才能继续给第二个。针对TransferQueue的tryTransfer方法圆通快递员要将小明的2个快递送货到门韵达快递员也想将小明的2个快递送货到门。发现小明不在家就把快递直接放到菜鸟驿站了。针对TransferQueue的tryTransfer超时方法圆通快递员要将小明的2个快递送货到门韵达快递员也想将小明的2个快递送货到门。发现小明不在家于是先等了5分钟发现小明还没有回来就把快递直接放到菜鸟驿站了。8.3 TransferQueue的原理解析transfer(E e)原理如下图所示transfer方法的原理原理图解释生产者线程Producer Thread尝试将元素B传给消费者线程如果没有消费者线程则将元素B放到尾节点。并且生产者线程等待元素B被消费。当元素B被消费后生产者线程返回。如果当前有消费者正在等待接收元素消费者通过take方法或超时限制的poll方法时transfer方法可以把生产者传入的元素立刻transfer传输给消费者。如果没有消费者等待接收元素transfer方法会将元素放在队列的tail尾节点并等到该元素被消费者消费了才返回。tryTransfer(E e)试探生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素则返回false。和transfer方法的区别是无论消费者是否接收方法立即返回。tryTransfer(E e, long timeout, TimeUnit unit)带有时间限制的tryTransfer方法。试图把生产者传入的元素直接传给消费者。如果没有消费者消费该元素则等待指定的时间再返回。如果超时了还没有消费元素则返回false。如果在超时时间内消费了元素则返回true。getWaitingConsumerCount()获取通过BlockingQueue.take()方法或超时限制poll方法等待接受元素的消费者数量。近似值。返回等待接收元素的消费者数量。hasWaitingConsumer()获取是否有通过BlockingQueue.tabke()方法或超时限制poll方法等待接受元素的消费者。返回true则表示至少有一个等待消费者。8.3 TransferQueue接口继承了哪些接口BlockingQueue接口可作为阻塞队列使用Queue接口可作为队列使用8.4 哪些类实现了TransferQueue接口LinkedTranferQueue接口九、优先由你PriorityQueue类 9.1 理解PriorityQueue类本应该按照升序排序本应该按照升序排序按照倒叙排序按照自定义优先级排序PriorityQueue是一个支持优先级的无界阻塞队列。默认自然顺序升序排序。可以通过构造参数Comparator来对元素进行排序。public PriorityQueue(Comparator? super E comparator) {this(DEFAULT_INITIAL_CAPACITY, comparator);
}
自定义实现comapreTo()方法来指定元素排序规则。public Comparator? super E comparator() {return comparator;
}
不允许插入null元素。实现PriorityQueue接口的类不保证线程安全除非是PriorityBlockingQueue。PriorityQueue的迭代器不能保证以任何特定顺序遍历元素如果需要有序遍历请考虑使用Arrays.sort(pq.toArray)。进列(offer、add)和出列 poll、remove()的时间复杂度O(log(n))。remove(Object) 和 contains(Object)的算法时间复杂度O(n)。peek、element、size的算法时间复杂度为O(1)。9.2 PriorityQueue类继承了哪些类AbstractQueue抽象类具有队列的功能9.2 PriorityQueue类实现了哪些接口Queue接口可作为队列使用。十、双向链表LinkedList类 10.1 LinkedList的结构LinkedList实现了List和Deque接口所以是一种双链表结构可以当作堆栈、队列、双向队列使用。一个双向列表的每一个元素都有三个整数值元素、向后的节点链接、向前的节点链接LinkedList的结构我们来看下节点类Nodeprivate static class NodeE {E item; //元素NodeE next; //向后的节点链接NodeE prev; //向前的节点链接Node(NodeE prev, E element, NodeE next) {this.item element;this.next next;this.prev prev;}
}
10.2 与ArrayList的区别1.LinkedList的增加和删除效率相对较高而查找和修改的效率相对较低。2.以下情况建议使用ArrayList频繁访问列表中的一个元素。只在列表的首尾添加元素。3.以下情况建议使用LinkedList频繁地在列表开头、中间、末尾添加和删除元素。需要通过循环迭代来访问列表中的元素。10.3 LinkedList不是线程安全的LinkedList不是线程安全的所以可以使用如下方式保证线程安全。List list Collections.synchronizedList(new LinkedList());
10.4 LinkedList的家庭成员关系LinkedList 继承了 AbstractSequentialList 类。LinkedList 实现了 Queue 接口可作为队列使用。LinkedList 继承了 AbstractQueue抽象类具有队列的功能。LinkedList 实现了 List 接口可进行列表的相关操作。LinkedList 实现了 Deque 接口可作为双向队列使用。LinkedList 实现了 Cloneable 接口可实现克隆。LinkedList 实现了 java.io.Serializable 接口即可支持序列化能通过序列化去传输。十一、并发安全ConcurrentLinkedQueue类 11.1 理解ConcurrentLinkedQueueConcurrentLinkedQueue原理ConcurrentLinked是由链表结构组成的线程安全的先进先出无界队列。当多线程要共享访问集合时ConcurrentLinkedQueue是一个比较好的选择。不允许插入null元素支持非阻塞地访问并发安全的队列不会抛出ConcurrentModifiationException异常。size方法不是准确的因为在统计集合的时候队列可能正在添加元素导致统计不准。批量操作addAll、removeAll、retainAll、containsAll、equals和toArray不保证原子性操作不可分割添加元素happen-before其他线程移除元素。用法如下ConcurrentLinkedQueue queue new ConcurrentLinkedQueue();
BuildingBlockWithName buildingBlock new BuildingBlockWithName(三角形, A);
concurrentLinkedQueue.add(buildingBlock);
11.2 ConcurrentLinkedQueue类继承了哪些类AbstractQueue抽象类具有队列的功能11.3 ConcurrentLinkedQueue类实现了哪些接口Queue接口可作为队列使用十二、双向数组ArrayDeque类 ArrayDeque原理图12.1 理解ArrayDeque由数组组成的双端队列。没有容量限制根据需要扩容。不是线程安全的。禁止插入null元素。当用作栈时比栈速度快当用作队列时速度比LinkList快。大部分方法的算法时间复杂度为O(1)。remove、removeFirstOccurrence、removeLastOccurrence、contains、remove 和批量操作的算法时间复杂度O(n)12.2 使用方法创建一个ArrayDeque往arrayDeque队尾添加元素。ArrayDeque arrayDeque new ArrayDeque();
for (int i 0; i 50; i) {arrayDeque.add(buildingBlock); // add方法等价于addLast方法
}
12.3 ArrayDeque实现了哪些接口Deque接口 - 可用于双端队列十三、双向并发ConcurrentLinkedDeque类 13.1 理解ConcurrentLinkedDeque类ConcurrentLinkedDeque原理图由链表结构组成的双向无界阻塞队列插入、删除和访问操作可以并发进行线程安全的类不允许插入null元素在并发场景下计算队列的大小是不准确的因为计算时可能有元素加入队列。批量操作addAll、removeAll、retainAll、containsAll、equals和toArray不保证原子性操作不可分割13.2 ConcurrentLinkedDeque使用示例创建两个积木三角形、四边形然后添加到队列BuildingBlockWithName buildingBlock1 new BuildingBlockWithName(三角形, A);
BuildingBlockWithName buildingBlock2 new BuildingBlockWithName(四边形, B);
ConcurrentLinkedDeque concurrentLinkedDeque new ConcurrentLinkedDeque();
concurrentLinkedDeque.addFirst(buildingBlock1);
concurrentLinkedDeque.addLast(buildingBlock2);
//结果顺序三角形、四边形
13.3 ConcurrentLinkedDeque实现了哪些接口Deque接口 - 可用于双端队列十四、数组阻塞ArrayBlockingQueue类 14.1 理解ArrayBlockingQueueArrayBlockingQueuey原理图ArrayBlockingQueue是一个用数组实现的有界阻塞队列。队列慢时插入操作被阻塞队列空时移除操作被阻塞。按照先进先出FIFO原则对元素进行排序。默认不保证线程公平的访问队列。公平访问队列按照阻塞的先后顺序访问队列即先阻塞的线程先访问队列。非公平性是对先等待的线程是非公平的当队列可用时阻塞的线程都可以争夺访问队列的资格。有可能先阻塞的线程最后才访问访问队列。公平性会降低吞吐量。14.2 ArrayBlockingQueue使用示例创建两个积木三角形、四边形然后添加到队列BuildingBlockWithName buildingBlock1 new BuildingBlockWithName(三角形, A);
BuildingBlockWithName buildingBlock2 new BuildingBlockWithName(四边形, B);
ArrayBlockingQueue arrayBlockingQueue new ArrayBlockingQueue(100, true);
arrayBlockingQueue.add(buildingBlock1);
arrayBlockingQueue.add(buildingBlock2);
14.3 ArrayBlockQueue实现了哪些接口Deque接口 - 可用于双端队列十五、链表阻塞LinkedBlockingQueue类 15.1 理解LinkedBlockingQueueLinkedBlockingQueue原理LinkedBlockingQueue具有单链表和有界阻塞队列的功能。队列慢时插入操作被阻塞队列空时移除操作被阻塞。默认和最大长度为Integer.MAX_VALUE相当于无界(值非常大2^31-1)。15.2 LinkedBlockingQueue使用示例LinkedList linkedList1 new LinkedList();
linkedList1.add(A);
linkedList1.add(B);
linkedList1.add(C);
15.3 LinkedBlockingQueue的应用场景吞吐量通常要高于ArrayBlockingQueue。创建线程池时参数runnableTaskQueue任务队列用于保存等待执行的任务的阻塞队列可以选择LinkedBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。15.4 LinkedBlockingQueue实现了哪些接口LinkedBlockingQueue继承了 BlockingQueue类可作为阻塞队列使用LinkedBlockingQueue继承了 AbstractQueue抽象类具有队列的功能。LinkedBlockingQueue实现了 java.io.Serializable 接口即可支持序列化能通过序列化去传输。十六、双向阻塞LinkedBlockingDeque类 16.1 理解LinkedBlockingDeque类LinkedBlockingDeque原理图由链LinkedBlockingDeque 阻塞队列链表双端访问线程安全。多线程同时入队时因多了一端访问入口所以减少了一半的竞争。默认容量大小为Integer.MAX_VALUE。可指定容量大小。16.2 LinkedBlockingDeque的应用场景LinkedBlockingDeque可以用在“工作窃取“模式中。工作窃取算法某个线程比较空闲从其他线程的工作队列中的队尾窃取任务来帮忙执行。16.3 LinkedBlockingDeque实现了哪些接口LinkedBlockingDeque继承了 BlockingDeque类可作为阻塞队列使用LinkedBlockingDeque继承了 AbstractQueue抽象类具有队列的功能。LinkedBlockingDeque实现了 java.io.Serializable 接口即可支持序列化能通过序列化去传输。十七、链表阻塞LinkedTransferQueue类 17.1 理解LinkedTransferQueue类LinkedTransferQueue原理图LinkedTransferQueue 阻塞队列链表结构TransferQueue之前我们讲“使命必达TransferQueue接口时已经介绍过了TransferQueue接口 所以LinkedTransferQueue接口跟它相似只是加入了阻塞插入和移除的功能以及结构是链表结构。之前的TransferQueue也讲到了3个案例来说明TransferQueue的原理大家可以回看TransferQueue。17.2 LinkedTransferQueue接口比其他阻塞队列多了5个方法transfer(E e)tryTransfer(E e)tryTransfer(E e, long timeout, TimeUnit unit)getWaitingConsumerCount()hasWaitingConsumer()17.3 LinkedTransferQueue代码示例创建一个LinkedTransferQueue生产者1 依次往队列中添加 A、B、C生产者1 依次往队列中添加 A、B、C生产者2 依次往队列中添加 D、E、F生产者2 依次往队列中添加 D、E、F消费者依次从队列首部开始消费元素每次消费前先sleep 2s来演示transfer方法是否进行了等待。消费者消费元素运行结果生产者1 transfer A
生产者2 transfer D 2s后...消费者 take A
生产者1 put B 2s后...消费者 take D
生产者2 transfer E 2s后...消费者 take B
生产者1 transfer C
代码执行结果分析1首先生产者线程1和2 调用transfer方法来传输A和D发现没有消费者线程接收所以被阻塞。2消费者线程过了2s后将A拿走了然后生产者1 被释放继续执行传输元素B发现又没有消费者消费所以进行了等待。3消费者线程过了2s后将排在队列首部的D元素拿走生产者2继续往下执行传输元素E发现没有消费者所以进行了等待。4消费者线程过了2s后将排在队列首部的B元素拿走生产者1传输C元素等待消费者拿走。5消费者不再消费了所以生产者1和生产者2都被阻塞了元素C和元素E都没有被拿走而且生产者2的F元素还没有开始传输因为在等待元素D被拿走。6看下队列里面确实有C和E元素而且E排在队列的首部。队列里面的元素17.4 LinkedTransferQueue实现了哪些接口LinkedBlockingDeque继承了 BlockingQeque类可作为阻塞队列使用LinkedBlockingDeque继承了 AbstractQueue抽象类具有队列的功能。十八、传球好手SynchronousQueue类 18.1 理解SynchronousQueue类SynchronousQueue原理图我称SynchronousQueue为”传球好手“。想象一下这个场景小明抱着一个篮球想传给小花如果小花没有将球拿走则小明是不能再拿其他球的。SynchronousQueue负责把生产者产生的数据传递给消费者线程。SynchronousQueue本身不存储数据调用了put方法后队列里面也是空的。每一个put操作必须等待一个take操作完成否则不能添加元素。适合传递性场景。性能高于ArrayBlockingQueue和LinkedBlockingQueue。18.2 SynchronousQueue示例我们创建了两个线程一个线程用于生产一个线程用于消费生产的线程依次put A、B、C三个值生产的线程依次put A、B、C三个值消费线程使用take来消费阻塞队列中的内容每次消费前等待5秒消费线程每隔5s调用take方法运行结果t1 put A
t2 take A 5秒后...t1 put B
t2 take B 5秒后...t1 put C
t2 take C
小结说明生产线程执行put第一个元素A 操作后需要等待消费者线程take完“A”后才能继续往下执行代码。18.1 SynchronousQueue应用场景吞吐量通常要高于LinkedBlockingQueue。创建线程池时参数runnableTaskQueue任务队列用于保存等待执行的任务的阻塞队列可以选择SynchronousQueue。静态工厂方法Executors.newCachedThreadPool()使用了这个队列18.2 SynchronousQueue和LinkedTransferQueue的区别SynchronousQueue 不存储元素而LinkedTransferQueue存储元素。SynchronousQueue 队列里面没有元素而LinkedTransferQueue可以有多个存储在队列等待传输。LinkedTransferQueue还支持若传输不了则丢到队列里面去。LinkedTransferQueue还支持若超过一定时间传输不了则丢到队列里面去。十九、优先级阻塞PriorityBlockingQueue类 19.1 理解PriorityBlockQueue类PriorityBlockQueue的原理图PriorityBlockQueue PriorityQueue BlockingQueue之前我们也讲到了PriorityQueue的原理支持对元素排序。元素默认自然排序。可以自定义CompareTo()方法来指定元素排序规则。可以通过构造函数构造参数Comparator来对元素进行排序。19.2 PriorityBlockQueue实现了哪些接口LinkedBlockingQueue继承了 BlockingQueue接口可作为阻塞队列使用LinkedBlockingQueue继承了 AbstractQueue抽象类具有队列的功能。LinkedBlockingQueue实现了 java.io.Serializable 接口即可支持序列化能通过序列化去传输。二十、延时阻塞DelayQueue类 20.1 理解DelayQueueDelayQueue原理图DelayQueue Delayed BlockingQueue。队列中的元素必须实现Delayed接口。public class DelayQueueE extends Delayed extends AbstractQueueEimplements BlockingQueueE {
在创建元素时可以指定多久可以从队列中获取到当前元素。只有在延时期满才能从队列中获取到当前元素。20.2 源码解析添加元素时指定延时多久可以从队列中获取元素public boolean offer(E e, long timeout, TimeUnit unit) {return offer(e);
}
获取元素的方法poll需要等待延时时间过了才能获取到元素if (first null || first.getDelay(NANOSECONDS) 0)return null;
elsereturn q.poll();
poll方法20.3 应用场景缓存系统的设计可以用DelayQueue保存缓存元素的有效期。然后用一个线程循环的查询DelayQueue队列一旦能从DelayQueue中获取元素时表示缓存有效期到了。定时任务调度使用DelayQueue队列保存当天将会执行的任务和执行时间一旦从DelayQueue中获取到任务就开始执行。比如Java中的TimerQueue就是使用DelayQueue实现的。20.4 DelayQueue实现了哪些接口DelayQueue实现了 BlockingQueue接口可作为阻塞队列使用这一篇花了很多心思在上面看官方英文文档、画原理图、写demo代码排版。这恐怕是市面上最全最细讲解Queue的。
往期推荐
3W字带你玩转「消息队列」2020-11-26 这8种常见的SQL错误用法你还在用吗2020-11-27 45 张图深度解析 Netty 架构与原理2020-12-02 求求你不要再使用!null判空了2020-12-01 关注我每天陪你进步一点点