在Java中如何使用Queue接口实现先进先出队列_Java队列基础解析

Java中Queue接口不提供默认FIFO阻塞行为,具体FIFO特性取决于实现类:ArrayDeque(推荐)、ConcurrentLinkedQueue或LinkedBlockingQueue等,需按线程安全与阻塞需求选择。

Java里Queue接口默认不提供FIFO的阻塞行为

Java的Queue接口本身只是定义了入队(add/offer)、出队(remove/poll)、查看头元素(element/peek)等契约,并不保证线程安全,也不强制FIFO——它只是“建议”实现为FIFO。实际是否FIFO,取决于你选的具体实现类。

常见误区是直接用new Queue(),这根本不能编译,因为Queue是接口。必须选一个实现类:

  • LinkedList:非线程安全,FIFO,适合单线程或已加锁场景
  • ArrayDeque:非线程安全,FIFO,性能优于LinkedList(推荐作为默认选择)
  • ConcurrentLinkedQueue:线程安全,无界,FIFO,但size()不是O(1)
  • LinkedBlockingQueue:线程安全,可选有界/无界,FIFO,支持阻塞操作(如take()

ArrayDeque比LinkedL

ist更适合做普通FIFO队列

ArrayDeque底层用循环数组,offer()poll()平均时间复杂度都是O(1),内存局部性好;而LinkedList是双向链表,每次操作要分配/释放节点对象,GC压力大,且缓存不友好。

除非你需要在队列中间插入或删除(这不是Queue接口的用途),否则别选LinkedList当队列用。它被设计成ListDeque双实现,但作为队列纯属“能用,不优”。

Queue queue = new ArrayDeque<>();
queue.offer("first");
queue.offer("second");
System.out.println(queue.poll()); // 输出 "first"
System.out.println(queue.poll()); // 输出 "second"

别在多线程环境里直接用ArrayDeque或LinkedList

这两个类都不保证线程安全。多个线程同时调用offer()poll()可能引发数据错乱、丢失元素,甚至ConcurrentModificationException(尤其遍历时)。

如果需要线程安全的FIFO队列,按场景选:

  • 高吞吐、不关心阻塞 → 用ConcurrentLinkedQueue
  • 需要阻塞等待(比如生产者-消费者模型)→ 用LinkedBlockingQueue,并明确指定容量避免无限增长
  • 需要公平锁或更多控制 → 考虑PriorityBlockingQueue(注意:它不是FIFO!)或自己包装加锁

错误示范:static Queue sharedQ = new ArrayDeque(); —— 多线程下必出问题。

offer()和add()的区别常被忽略

两者都用于入队,但异常策略不同:

  • add():失败时抛IllegalStateException(比如有界队列满时)
  • offer():失败时返回false,更利于程序判断和恢复

同理,remove() vs poll()(空队列时前者抛NoSuchElementException,后者返回null),element() vs peek()(前者抛异常,后者返回null)。

日常编码中,优先用offer()/poll()/peek(),它们更健壮,也符合“队列可能满或空”的现实假设。

FIFO只是Queue的一种语义约定,Java不会替你校验实现是否真按顺序进出;选错实现类、忽略线程安全、混淆addoffer的异常行为,这三个点最容易在真实项目里埋雷。