队列

队列

LinkedBlockingQueue 阻塞队列

  • 新增操作
    • add队列满的时候抛出异常
    • offer队列满的时候返回false
  • 查看并删除
    • remove 队列空的时候抛出异常
    • poll 队列空的时候返回null
  • 只查看不删除操作
    • element队列空的时候抛异常
    • peek 队列空的时候返回null
  • 类描述
    • 基于链表的阻塞队列,其底层的数据结构是链表
    • 链表维护先入先出队列,新元素被放在队尾,获取元素从队头拿。
    • 链表大小在初始化的时候可以设置,默认是Integer的最大值
    • 可以使用Collection和Iterator两个接口的所有操作,因为实现了两者的接口
  • 内部构成
    • 链表存储 + 锁 + 迭代器
    • 链表的作用是保存当前节点,节点中的数据可以是任意 东西是一个泛型,比如说队列被应用到线程池时,节点就是线程。比如队列被应用到消息队列上,节点就是消息,节点的含义主要看队列被使用的场景
    • 锁有take锁和put锁 是为了保证队列操作时的线程安全,设计两种锁,是为了take和put两种操作可以同时进行 互不影响

SynchronousQueue 交换队列

  • 类描述
    • 队列不存储数据,所以没有大小 无法迭代
    • 插入操作的返回必须等待另一个线程完成对应数据的删除操作,反之亦然。
    • 队列由两种数据结构祖册,分别是后入先出的堆栈和先入先出的队列。堆栈是非公平的,队列是公平的。
    • 默认无参构造器是非公平的
  • 栈实现
    • 其阻塞的策略,并不是一上来就阻塞住,而是在自旋一定次数后,仍然没有其它线程来满足自己的要求时,才会真正的阻塞住。
  • 队列实现
    • 次 put 数据的时候,都 put 到队尾上,而每次拿数据时,并不是直接从队头拿数据,而是从队尾往前寻找第一个被阻塞的线程,这样就会按照顺序释放被阻塞的线程

DelayQueue 延时队列

  • 类描述
    • 队列中元素将在过期时被执行,越靠近队头,越早过期。
    • 未过期的元素不能够被take
    • 不允许空元素
  • 实现要求
    • delayQueue中的元素必须是Delayed的子类;Delayed是表达延迟能力的关键接口 其继承了Comparable接口 并定义了还剩多久过期的方法
    • 内部排序用的是ProorityQueue队列 超时阻塞使用的事锁的等待能力

ArrayBlockingQueue 有界循环队列

  • 类描述
    • 有界的阻塞数组,容器一旦被创建,后续大小无法被修改
    • 元素士有序的,按照先入先出进行排序,从队尾插入数据,从队头拿数据
    • 队列满时,往队列中put 数据会被阻塞,从队列中拿数据也会被阻塞
  • 数据结构
    • takeIndex
    • putIndex
    • removeIndex
    • private final ReentrankLock lock (可重入锁)
    • private final Condition notEmpty (take的队列)
    • private final Condition notFull (put的队列)

常见面试题

  • 队列 如何实现阻塞的?
    • 队列本身并没有实现阻塞的功能,而是利用 Condition 的等待唤醒机制,阻塞底层实现就是更改线程的状态为沉睡。
  • 工作中经常使用队列put take方法有什么危害 如何避免?
    • 两个方法都是无限阻塞 没有超时的意思,容易使得线程全部阻塞住,大流量时,导致机器无线程可用,所以在大流量的时候使用offer 和poll 方法来代替两者,只要设置好超时时间,在规定时间内未获取到值就会返回默认值,这样不会导致流量大时,所有线程都阻塞住了。
  • 数据入队之后有没有办法让队列过一会在执行?
    • 可以的,DelayQueue 提供了这种机制,可以设置一段时间之后再执行。
    • 缺点:无法持久化,容易数据丢失,一般使用中间件的中间件会进行持久化
  • DelayQueue对元素有要求么,把String可以放到队列中么?
    • DelayQueue要求必须实现Delayed接口,Delayed 本身又实现了 Comparable 接口 , Delayed 接 口 的 作 用 是 定 义 还 剩 下 多 久 就 会 超 时 , 给 使 用 者 定 制 超 时 时 间 的 ,Comparable 接口主要用于对元素之间的超时时间进行排序的,两者结合,就可以让越快过期的元素能够排在前面。所以把 String 放到 DelayQueue 中是不行的,编译都无法通过,DelayQueue 类在定义的时候,是有泛型定义的,泛型类型必须是 Delayed 接口的子类才行。
  • 如何看SynchronousQueue队列的大小?
    • 无容量队列,其内部size方法返回的是0
  • synchronousQueue 底层有两种数据结构,分别是队列和栈
    • 队列维护了先入先出
    • 栈先入后出
  • 假设 SynchronousQueue 底层使用的是堆栈,线程 1 执行 take 操作阻塞住了,然后有线程 2 执行 put 操作,问此时线程 2 是如何把 put 的数据传递给take 的?
  • 首先线程 1 被阻塞住,此时堆栈头就是线程 1 了,此时线程 2 执行 put 操作,会把 put 的数据赋值给堆栈头的 match 属性,并唤醒线程 1,线程 1 被唤醒后,拿到堆栈头中的 match 属性,就能够拿到 put 的数据了
posted @ 2024-04-18 16:46  贺艳峰  阅读(2)  评论(0编辑  收藏  举报