ExecutorService的OOM坑

探讨了Java中ExecutorService的不同创建方式及默认配置可能导致的问题。推荐使用ThreadPoolExecutor以更好地控制线程池和任务队列。

ExecutorService有下面几种创建方式

                   Executors.newCachedThreadPool();

                   Executors.newFixedThreadPool()

                   Executors.newScheduledThreadPool()

                   Executors.newSingleThreadExecutor()

                   Executors.newSingleThreadScheduledExecutor()

抽取其中一个方法查看源码:

publicstatic ExecutorService newFixedThreadPool(int nThreads) {

    return new ThreadPoolExecutor(nThreads,nThreads,

                                  0L, TimeUnit.MILLISECONDS,

                                  newLinkedBlockingQueue<Runnable>());

}

   /**

     * Creates a {@code LinkedBlockingQueue}with a capacity of

     * {@link Integer#MAX_VALUE}.

     */

   publicLinkedBlockingQueue(){

        this(Integer.MAX_VALUE);

    }

 

   /**

     * A constant holding the maximum value an{@code int} can

     * have, 2<sup>31</sup>-1.

     */

    public static final int   MAX_VALUE = 0x7fffffff;

 

 

由上面的方法参数知道,Executors创建的ExecutorService并不能设置控制消息队列的长列,默认长度是2^31当线程处理速度较慢且并发任务太多时,任务队列的任务将不断堆积,就可能引起OOM。因此建议直接

 

ThreadPoolExecutor executor = new TThreadPoolExecutor(int corePoolSize,

                              intmaximumPoolSize,

                              longkeepAliveTime,

                              TimeUnit unit,

                             BlockingQueue<Runnable> workQueue)

这样就可以灵活定制队列,建议使用new ThreadPoolExecutor。

 


### Android 中 ExecutorService 的使用示例 `ExecutorService` 是 Java 并发包中的一个重要接口,用于管理线程池并执行异步任务。它允许开发者通过提交任务来控制并发行为,而无需手动创建和销毁线程。 #### 创建 `ExecutorService` 可以通过 `Executors` 工厂类创建不同类型的线程池: ```java // 单一线程池:按顺序执行任务 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); // 固定大小线程池:指定最大并发数 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); // 缓存线程池:根据需要创建新线程 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); ``` 上述代码展示了如何利用工厂方法快速构建适合需求的线程池[^1]。 #### 提交任务到 `ExecutorService` 可以向 `ExecutorService` 提交两种类型的任务:无返回值的任务 (`Runnable`) 和有返回值的任务 (`Callable`)。 ##### 使用 `Runnable` 接口 对于不需返回结果的任务,可实现 `Runnable` 接口并将其实例传递给 `execute()` 方法: ```java singleThreadExecutor.execute(() -> { System.out.println("Task running on a separate thread"); }); ``` 此代码片段展示了一个简单的匿名内部类作为任务运行于单独线程上[^2]。 ##### 使用 `Callable` 接口 如果希望获取任务的结果,则应使用 `submit(Callable<T>)` 方法代替 `execute(Runnable)`: ```java Future<Integer> futureResult = fixedThreadPool.submit(() -> { int result = 0; for (int i = 1; i <= 10; i++) { result += i; } return result; }); try { Integer sum = futureResult.get(); // 阻塞直到计算完成 System.out.println("Sum of numbers from 1 to 10 is: " + sum); } catch (Exception e) { e.printStackTrace(); } ``` 这里说明了如何通过 `Future` 对象等待任务结束并取得其最终成果[^3]。 #### 关闭 `ExecutorService` 当不再需要继续调度新的任务时,应当调用 `shutdown()` 或者更强制性的 `shutdownNow()` 来释放资源: ```java fixedThreadPool.shutdown(); boolean terminated = false; try { terminated = fixedThreadPool.awaitTermination(60, TimeUnit.SECONDS); // 等待最多一分钟 } catch (InterruptedException ie) {} if (!terminated) { fixedThreadPool.shutdownNow(); // 尝试立即停止所有正在执行的任务 } ``` 这部分强调了合理关闭服务的重要性以及具体操作方式[^4]。 --- ### 常见问题及其解决方案 1. **忘记关闭线程池** 如果未显式调用 `shutdown()`, 则可能导致内存泄漏或程序无法正常退出。务必始终记得在线程池生命周期结束后将其关闭。 2. **处理异常不当** 当任务抛出未经捕获的异常时,默认情况下这些异常会被忽略。建议为每个任务设置自定义的 `UncaughtExceptionHandler`. 3. **过度依赖缓存线程池** 虽然 `newCachedThreadPool` 很方便,但它可能因频繁创建大量短命线程而导致性能下降甚至 OOM 错误。考虑改用带有限制条件的固定大小线程池替代之。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值