Java 线程池内存队列满了怎么办
0 概述
- Java 线程池是处理并发编程的重要工具,而内存队列作为线程池的核心组件之一,用于存储待处理的任务。当内存队列满时,意味着线程池已达到其处理能力的上限。如何处理这种情况是确保系统稳定和高效的关键。
1 内存队列满的原因及影响
- 原因分析:
- 任务过多:提交给线程池的任务量超过了线程池的处理能力。
- 线程池配置不当:核心线程数、最大线程数、队列大小等配置不匹配实际需求。
- 任务执行时间长:某些任务执行时间过长,导致队列迅速填满。
- 影响:
- 系统性能下降:新任务无法及时处理,系统吞吐量降低。
- 资源耗尽:长时间满队列可能导致系统资源耗尽,如内存溢出。
- 系统稳定性下降:可能导致程序崩溃或不稳定状态。
2 一些解决方案
-
增加队列容量: 如果队列满了,一个简单的解决方案是增加队列的容量。这可以通过在创建线程池时设置一个更大的队列来实现。
// 创建一个固定大小为10的线程池,队列容量为1000 ExecutorService executor = Executors.newFixedThreadPool(10, 1000);
-
动态调整线程池参数: 当队列快满时,可以动态地增加线程池的大小。这可以通过实现自定义的
RejectedExecutionHandler
来实现ThreadPoolExecutor executor = new ThreadPoolExecutor(...); executor.setRejectedExecutionHandler((r, executor) -> { // 处理被拒绝的任务,例如重新提交任务到队列中等待执行 });
-
使用阻塞策略: 当队列满了,但任务仍然在不断提交时,可以使用阻塞策略。可以阻塞提交任务的线程,直到队列中有空间为止
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(1000); executor.submit(() -> { try { queue.take(); // 阻塞直到队列中有空间为止 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } });
-
拒绝任务: 当队列满了并且无法扩大时,可以选择优雅地拒绝新任务。也是通过实现自定义的
RejectedExecutionHandler
来实现ThreadPoolExecutor executor = new ThreadPoolExecutor(...); executor.setRejectedExecutionHandler((r, executor) -> { // 处理被拒绝的任务,例如记录日志、返回一个默认值或者抛出一个异常等 });
-
监控和预警: 通过监控工具来定期检查线程池的状态,当队列使用率接近或达到警戒线时,发出预警。这可以帮助及时发现和解决问题,可以选择
JMX
来监控线程池的状态ThreadPoolExecutor executor = new ThreadPoolExecutor(...); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("com.example:type=ThreadPoolExecutor,name=myExecutor"); StandardMBean mbean = new StandardMBean(executor, StandardMBean.class); mbs.registerMBean(mbean, name); // 注册MBean以供监控
3 演示 Demo
-
下面将对自定义拒绝策略进行演示
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * @Author Jasper * @Time 2024/2/3 * @公众号:EzCoding */ public class RejectDemo { private static final int CAPACITY = 5; // 初始队列容量 private static final int MAX_TRY_COUNT = 3; // 最大重试次数 private static final LinkedBlockingQueue<Runnable> queue; private static final ThreadPoolExecutor executor; // 简单的重新入队策略 private static final RejectedExecutionHandler handler = (r, executor) -> { if (!executor.isShutdown()) { try { // 等待一段时间后重新提交任务 System.out.println("任务 " + r.toString() + " 被拒绝,准备重新提交"); Thread.sleep(1000); // 等待1秒 executor.getQueue().put(r); // 重新提交任务到队列 } catch (InterruptedException e) { // 如果等待被中断,则不再重新提交 Thread.currentThread().interrupt(); } } }; // 包含重试次数的重新入队策略 private static final RejectedExecutionHandler retryHandler = (r, executor) -> { ConcurrentHashMap<Runnable, AtomicInteger> map = new ConcurrentHashMap<>(); if (!executor.isShutdown()) { AtomicInteger count = map.computeIfAbsent(r, k -> new AtomicInteger(0)); int tryCount = count.incrementAndGet(); if (tryCount <= MAX_TRY_COUNT) { try { System.out.println("任务 " + r + " 被拒绝,第 " + tryCount + " 次重试"); Thread.sleep(1000); // 等待1秒 executor.getQueue().put(r); // 重新提交任务到队列 } catch (InterruptedException e) { // 如果等待被中断,则不再重新提交 Thread.currentThread().interrupt(); } } else { System.out.println("任务 " + r.toString() + " 达到最大重试次数 " + MAX_TRY_COUNT + ",放弃该任务"); // 可以选择抛出异常、记录日志或其他处理方式... } } }; static { queue = new LinkedBlockingQueue<>(CAPACITY); executor = new ThreadPoolExecutor( 1, // 核心线程数 2, // 最大线程数 60, TimeUnit.SECONDS, queue, // 阻塞队列 r -> { // 线程工厂 AtomicInteger threadNumber = new AtomicInteger(1); Thread thread = new Thread(r); thread.setName("Thread-" + threadNumber.getAndIncrement()); thread.setDaemon(false); // 设置为非守护线程,以避免程序退出时中断任务 return thread; }, retryHandler ); } public static void doTask(int taskId) { try { System.out.println("正在执行任务 " + taskId); Thread.sleep(500); // 模拟任务执行时间 System.out.println("任务 " + taskId + " 执行完成"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { final int taskId = i; executor.submit(() -> doTask(taskId)); } // 为了防止主线程退出,导致线程池中的线程被中断,可以等待线程池中的所有任务执行完成 try { // 等待所有任务执行完成,包括队列中的任务 executor.shutdown(); if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (Exception e) { e.printStackTrace(); } } } /** * 输出: * 正在执行任务 6 * 正在执行任务 0 * 任务 java.util.concurrent.FutureTask@68de145[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@4b85612c[Wrapped task = top.ezjava.java17demo.thread.task.RejectDemo$$Lambda$18/0x0000027281003218@277050dc]] 被拒绝,第 1 次重试 * 任务 6 执行完成 * 正在执行任务 1 * 任务 0 执行完成 * 正在执行任务 2 * 任务 1 执行完成 * 正在执行任务 3 * 任务 2 执行完成 * 正在执行任务 4 * 任务 3 执行完成 * 正在执行任务 5 * 任务 4 执行完成 * 正在执行任务 7 * 任务 5 执行完成 * 正在执行任务 8 * 任务 7 执行完成 * 正在执行任务 9 * 任务 9 执行完成 * 任务 8 执行完成 */
-
以上就是 Java 线程池内存队列满了如何处理的全部内容了,不知道你们在平时是怎么处理队列打满问题的呢,欢迎在评论区讨论~
-
创作不易,感谢阅读,若遇到问题,可以关注此微信gzh:EzCoding 留言反馈,希望能够帮助到您