面试题答案
一键面试性能表现对比
- ConcurrentLinkedQueue
- 并发插入:基于链表结构,无锁实现,在高并发插入场景下,由于不需要获取锁,不会因为锁竞争而阻塞线程,插入性能较好。但由于链表节点动态分配内存,频繁插入可能导致内存碎片。
- 并发删除:同样无锁,删除操作通过修改链表指针完成,性能也不错,但如果删除操作过于频繁,链表结构调整可能带来一定开销。
- 并发查询:由于无锁且为链表结构,查询时需要从头遍历链表,在数据量较大时查询效率较低。而且在遍历过程中,如果有其他线程进行插入或删除操作,可能会影响遍历结果的一致性。
- ArrayBlockingQueue
- 并发插入:基于数组结构,有界队列,使用ReentrantLock实现线程安全。在高并发插入时,由于锁的存在,可能会出现锁竞争,导致性能瓶颈,尤其是当并发度较高时。但数组结构在内存分配上相对连续,不会像链表那样产生过多内存碎片。
- 并发删除:同样受锁影响,删除操作需要获取锁,在高并发下可能存在性能问题。
- 并发查询:可以通过数组索引直接访问元素,查询效率较高。但在并发环境下,查询操作同样需要获取锁,可能会受到其他线程的锁竞争影响。
- LinkedBlockingQueue
- 并发插入:基于链表结构,有界队列(也可通过构造函数设置为无界),使用两把锁(putLock和takeLock)分别控制插入和删除操作,在一定程度上减少了锁竞争。在高并发插入时,相比ArrayBlockingQueue,由于锁粒度更细,性能可能更好。但链表结构的动态内存分配仍可能导致内存碎片问题。
- 并发删除:通过takeLock控制,由于锁粒度较细,并发删除性能相对ArrayBlockingQueue可能更好。
- 并发查询:和ConcurrentLinkedQueue类似,需要遍历链表,查询效率较低,且并发环境下遍历可能受其他线程操作影响。
不同业务侧重点下的队列选择及原因
- 侧重于插入速度
- 选择ConcurrentLinkedQueue:原因是其无锁实现,在高并发插入场景下能避免锁竞争带来的性能损耗,从而获得较高的插入速度。虽然可能产生内存碎片,但在插入速度优先的场景下,这一缺点相对可以接受。
- 侧重于查询效率
- 选择ArrayBlockingQueue:由于它基于数组结构,可通过索引直接访问元素,查询效率高。尽管并发操作受锁影响,但如果查询操作远多于插入和删除操作,整体性能仍能得到保证。在这种情况下,锁竞争对性能的影响相对较小,而数组结构带来的查询优势更为突出。
- 兼顾插入和删除性能,且有界队列需求
- 选择LinkedBlockingQueue:其两把锁的设计减少了插入和删除操作之间的锁竞争,相比ArrayBlockingQueue在高并发的插入和删除场景下性能更好。同时,有界队列特性可有效控制内存使用,适用于需要对队列大小进行限制的场景。