Java中的队列都有哪些,有什么区别,java队列用法
如何解决写爬虫IP受阻的问题?立即使用。
队列是一种特殊的线性表,遵循“先进先出”的原则。在我们的日常使用中,经常用来并发操作数据。在并发编程中,有时有必要使用线程安全队列。如果要实现线程安全队列,通常有两种方式:一种是使用阻塞队列,另一种是使用线程同步锁。
什么是阻塞队列?
假设有一家面包店,客人吃面包,师傅烤面包。篮子里最多有两个面包,主人考完试就把面包放进篮子里,客人吃的时候就把面包从篮子里拿出来。为了保证客人吃面包或者主人烤面包的时候篮子不会溢出,这个时候就需要引用阻塞队列的概念,也就是我们常说的生产者-消费者模型。
队列是支持两个附加操作的队列。这两个附加操作支持阻塞插入和移除方法。
(1)支持阻塞的Insert方法:是指当队列满时,队列会阻塞插入元素的线程,直到队列满为止。
(2)支持阻塞移除方法:是指当队列为空时,获取元素的线程会等待队列变为非空。阻塞队列经常用在生产者和消费者的场景中。生产者是向队列中添加元素的线程,消费者是从队列中获取元素的线程。队列是一个容器,生产者用来存储元素,消费者用来获取元素。
系统内不阻塞队列:PriorityQueue 和 ConcurrentLinkedQueue
我们来看看非阻塞队列的关系(以PriorityQueue为例):
PriorityQueue类继承自AbstractQueue并实现Serializable接口。本质上,维护一个有序列表。PriorityQueue位于Java util包中。名字前半部分的优先二字就是优先的意思。其实这个队列是有“优先权”的。添加到队列中的元素根据其自然排序(通过其java.util.Comparator并行实现)或根据传递给构造函数的java.util.Comparator实现来定位。
ConcurrentLinkedQueue是基于链接节点的线程安全队列。并发访问不需要同步。因为它在队列末尾添加元素,从头部删除元素,所以不需要知道队列的大小,ConcurrentLinkedQueue对public集合的共享访问也能很好地工作。收集关于队列大小的信息会很慢,需要遍历队列;ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列。它使用先进先出规则对节点进行排序。当我们添加一个元素时,它将被添加到队列的末尾;当我们得到一个元素时,它将返回队列头的元素。
实现阻塞接口的队列:
BlockingQueue接口和五个阻塞队列类被添加到java.util.concurrent中,它本质上是一个稍有失真的FIFO数据结构。线程不是立即在队列中添加或删除元素,而是阻塞执行,直到空间或元素可用。
这五个队列提供的是不同的:
ArrayBlockingQueue:由数组支持的有界队列。
LinkedBlockingQueue:链接节点支持的可选有界队列。
PriorityBlockingQueue:优先级堆支持的无限制优先级队列。
DelayQueue:优先级堆支持的基于时间的调度队列。
SynchronousQueue:使用BlockingQueue接口的简单会合机制。
我们来看看ArrayBlockingQueue和LinkedBlockingQueue的继承关系:
看两个类的继承关系,可以知道它们也是从AbstractQueue继承,实现Serializable接口;不同的是,它们还实现了BlockingQueue接口。
简单介绍其中几个:
linkblockingQueuelinkedblockingQueue的默认大小是整数。MAX_VALUE,可以理解为缓存的有界等待队列。您可以选择指定其最大容量。它是一个基于链表的队列,按照FIFO(先进先出)对元素进行排序。当生产者将一段数据放入队列时,它被缓存在队列中。当队列缓冲区达到最大缓存容量(LinkedBlockingQueue可以通过构造函数指定该值)时,生产者队列将被阻塞,直到消费者消耗完队列中的一段数据,生产者线程将被唤醒,反之亦然。
ArrayBlockingQueue在构造时需要指定容量,可以选择是否需要公平性。如果公平性参数设置为true,那么等待时间最长的线程将首先被处理(实际上,这种公平性是通过将ReentrantLock设置为true来实现的:即等待时间最长的线程将首先运行)。通常,公平会以性能为代价,你应该只在真正需要的时候使用它。它是一个基于数组的阻塞循环队列,按照FIFO(先进先出)原则对元素进行排序。
PriorityBlockingQueue是优先级队列,而不是先进先出队列。元素按优先级顺序移除,队列没有上限。(看源代码,PriorityBlockingQueue是PriorityQueue的重新打包,基于堆数据结构。PriorityQueue没有容量限制,就像ArrayList一样,所以放上优先级阻塞队列也不会阻塞。尽管该队列在逻辑上是无限的,但是尝试执行添加操作可能会导致OutOfMemoryError,因为资源已经耗尽。但是,如果队列为空,获取元素的take操作将被阻塞,因此其检索操作take将被阻塞。另外,队列中的元素要有比较能力。
关于ConcurrentLinkedQueue和LinkedBlockingQueue:
也可以理解为阻塞队列和非阻塞队列的区别:
1.LinkedBlockingQueue使用锁机制,而ConcurrentLinkedQueue使用CAS算法,尽管LinkedBlockingQueue的底层锁获取也使用CAS算法。
2.关于取数元素,ConcurrentLinkedQueue不支持阻塞取数元素,LinkedBlockingQueue支持阻塞take()方法。
3.关于插入元素的性能,但是在实际使用过程中,尤其是在多cpu服务器上,体现出锁定和解锁的差距。ConcurrentLinkedQueue将比LinkedBlockingQueue快得多。
生产者消费者代码:
在网上看到一个生产者和消费者的小例子,对理解阻塞队列很有帮助。代码如下:
导入Java . util . concurrent . arrayblockingqueue;
导入Java . util . concurrent . blocking queue;
导入Java . util . concurrent . executorservice;
导入Java . util . concurrent . executors;
公共类BlockingQueueTest {
公共静态类篮{
BlockingQueueString basket=new ArrayBlockingQueue(3);
私有void produce()引发InterruptedException {
basket . put( apple );
}
私有void consume()引发InterruptedException {
basket . take();
}
private int getAppleNumber() {
return basket . size();
}
}
私有静态void testBasket() {
最终篮子篮子=新篮子();
类生成器实现Runnable {
公共无效运行(){
尝试{
while (true) {
System.out.println(生产者开始生产苹果# # );
basket . produce();
System.out.println(生产者生产完苹果# # );
System.out.println(篮子里的苹果数: basket . getapplenumber() one );
thread . sleep(300);
}
} catch(中断异常e) {}
}
}
类使用者实现Runnable {
公共无效运行(){
尝试{
while (true) {
System.out.println(消费者开始消费苹果* * * );
basket . consume();
System.out.println(消费者消费苹果完毕***);
System.out.println(篮子中的苹果数量: basket.getAppleNumber()个);
线程。睡眠(1000);
}
} catch(中断异常e) {}
}
}
执行者服务=执行者。newcachedthreadpool();
生产者生产者=新生产者();
消费者消费者=新消费者();
服务提交(生产者);
服务提交(消费者);
尝试{
线程。睡眠(10000);
} catch(中断异常e) {}
服务。立即关闭();
}
公共静态void main(String[] args) {
blockingqueuetest。测试篮();
}
}众多爪哇岛培训视频,尽在服务器端编程语言(专业超文本预处理器的缩写)中文网,欢迎在线学习!以上就是爪哇岛队列是什么的详细内容,更多请关注我们其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。