面试题答案
一键面试ArrayBlockingQueue特性
- 有界性:它是一个有界队列,在创建时需要指定容量大小。例如
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
,这就创建了一个容量为10的队列。 - 数组实现:内部使用数组来存储元素,这使得在遍历和查找元素时具有较好的性能,但是插入和删除操作可能需要移动元素,在高并发场景下性能可能不如链表实现的队列。
- 公平性:可以通过构造函数设置是否公平,默认是非公平的。公平性指等待时间最长的线程会优先获取锁。如果设置为公平,会降低吞吐量,但能保证线程等待时间的公平性。
LinkedBlockingQueue特性
- 可选有界性:既可以创建有界队列,如
LinkedBlockingQueue<Integer> boundedQueue = new LinkedBlockingQueue<>(10);
,也可以创建无界队列,如LinkedBlockingQueue<Integer> unboundedQueue = new LinkedBlockingQueue<>();
,无界队列理论上可以容纳无限个元素。 - 链表实现:基于链表结构存储元素,插入和删除操作不需要移动大量元素,性能较好,但遍历操作相对较慢。
- 线程安全:内部使用ReentrantLock和Condition来保证线程安全。
与OOM问题的关联
- ArrayBlockingQueue:如果设置的容量过小,可能导致任务不能及时入队,线程池会创建更多线程来处理任务,最终可能导致线程过多耗尽系统资源引发OOM。如果容量设置过大,可能会占用过多内存,当队列长时间处于满状态且任务不断产生时,也可能导致OOM。
- LinkedBlockingQueue:无界的
LinkedBlockingQueue
在任务不断产生且处理速度较慢时,队列会不断增长,最终耗尽内存导致OOM。有界的LinkedBlockingQueue
与ArrayBlockingQueue
类似,容量设置不合理也可能引发OOM。
避免OOM的优化策略
- ArrayBlockingQueue
- 合理设置容量:通过对系统负载和任务处理速度的分析,设置合适的队列容量。可以进行性能测试,逐步调整容量大小,找到一个平衡点,既能保证任务的缓冲,又不会占用过多内存。
- 监控与动态调整:可以定期监控队列的使用情况,如当前队列大小、任务入队和出队的频率等。当发现队列使用率过高时,可以考虑动态调整线程池的线程数量,以加快任务处理速度。
- LinkedBlockingQueue
- 尽量使用有界队列:避免使用无界队列,除非明确知道系统不会产生过多任务。对于有界队列,同样要合理设置容量,参考
ArrayBlockingQueue
容量设置的方法。 - 任务拒绝策略:当队列满时,合理设置任务拒绝策略,如
AbortPolicy
(直接抛出异常)、DiscardPolicy
(丢弃任务)、DiscardOldestPolicy
(丢弃队列中最老的任务)、CallerRunsPolicy
(让调用者线程处理任务)。根据业务需求选择合适的拒绝策略,避免任务无限制堆积。
- 尽量使用有界队列:避免使用无界队列,除非明确知道系统不会产生过多任务。对于有界队列,同样要合理设置容量,参考