述
除了集合,还有一种数据结构就是队列,前面在学习线程池的时候已经接触过了,用队列可以在线程之间传递数据,最常见的就是生产者和消费者模式,队列又分为阻塞队列和非阻塞队列
阻塞队列和非阻塞队列
队列是一个逻辑结构,对应的物理结构可能是由数组或链表实现的
那么阻塞队列和非阻塞队列有什么区别
举个例子,现在有一个队列,长度是10,现在已经满了,当第11个数据放进去的时候,阻塞队列可以设置一个超时时间,如果在这个时间内,队列中有数据出队了,那他就能放进去了,如果超时就放弃,非阻塞队列直接就丢失了,不会等
对于出队来说,也就是从队列中获取数据,如果是非阻塞队列,队列中没数据的时候取出来的是null,而阻塞队列会等,等到有数据了取出来
阻塞队列
阻塞队列的实现有 ArrayBlockingQueue
,LinkedBlockingQueue
,PriorityBlockingQueue
,SynchronousQueue
, 阻塞队列是线程安全的,阻塞队列又分为有界无界,无界队列的容量是 Integer.MAX_VALUE
常用方法
take()
: 获取并移除队列头节点,如果队列中没有数据,就阻塞,直到队列中有数据了put()
: 向队列中插入元素,如果满了,就阻塞add()
: 向队列中添加元素,如果满了就抛异常remove()
: 移除元素,如果是空也抛异常element()
: 获取头结点,如果是空也会抛异常offer()
: 向队列添加元素,如果满了就返回falsepoll()
: 取出元素,如果空就返回null,取出的同时会删除这个元素peek()
: 取出元素,如果空就返回null,取出的同时不会删除这个元素
ArrayBlockingQueue
ArrayBlockingQueue
是一个有界阻塞队列,可以指定容量,同时可以指定公平与非公平,如果是保证公平的话,那么等待了最长时间的线程会被优先处理,不过这会同时带来一定的性能损耗.
案例
新建一个长度为3的队列,然后生产者线程往里放,消费者线程往出取
1 | @Slf4j |
LinkedBlockingQueue
LinkedBlockingQueue
是一个无界的阻塞队列,容量是 Integer.MAX_VALUE
,数据是一个链表结构的,看一下他的源码
首先是一个内部类 Node
,用来存放数据
这里就可以看出来他是一个链表结构的
然后是两个重要的属性
这里可以看到, put 和 take 是两把锁,所以放入和取出互不干扰,最后再看看 put 方法.
具体用法跟上面的 ArrayBlockingQueue
类似
PriorityBlockingQueue
PriorityBlockingQueue
是一个支持优先级的阻塞队列,可以自定义顺序,也是一个无界队列, PriorityQueue
的线程安全版本
SynchronousQueue
SynchronousQueue
这个队列的容量为0,它不会取持有元素,做的事情就是直接投递数据,这个队列没有 peek()
等函数,因为它没有头节点,我们之前用的可缓存线程池就是用的这个队列
延迟队列
队列对应的还有延迟队列, Dueue
,根据延迟时间排序.元素需要实现 Delayed
接口
非阻塞并发队列
非阻塞并发队列只有一个 ConcurrentLinkedQueue
,链表数据结构,使用CAS非阻塞算法实现线程安全,适合在性能要求较高的并发场景,用的相对较少