Java中ScheduledExecutorService引发无法创建本地线程OOM问题咨询
问题分析与解决方案
这个逻辑很大概率是引发OOM错误的核心原因,具体分析和解决办法如下:
为什么会出现OOM?
每次调用ExecutorService.newFixedThreadPool(3)都会创建一个全新的线程池,这个线程池里的3个核心线程默认不会自动销毁(FixedThreadPool的核心线程数等于最大线程数,线程空闲时不会被回收)。如果定时任务每次执行完后,没有主动调用shutdown()或shutdownNow()关闭线程池,这些线程会一直处于存活状态。
随着定时任务一次次触发,存活的线程数量会以每次3个的速度持续累加,直到系统的线程资源被耗尽,JVM无法再创建新的本地线程,就会抛出java.lang.OutOfMemoryError: unable to create new native thread。
解决办法
方案1:复用线程池(推荐)
不要每次定时任务都新建线程池,改成全局单例的方式,程序启动时创建一次,之后所有定时任务都复用这个线程池:
// 全局单例线程池,仅初始化一次 private static final ExecutorService FIXED_THREAD_POOL = Executors.newFixedThreadPool(3); // 定时任务逻辑里直接复用线程池 public void scheduledTask() { FIXED_THREAD_POOL.submit(task1); FIXED_THREAD_POOL.submit(task2); FIXED_THREAD_POOL.submit(task3); }
注意:如果你的应用是Web服务或需要优雅停机,记得在应用关闭时调用FIXED_THREAD_POOL.shutdown()回收线程资源。
方案2:用完即关闭线程池
如果必须每次创建新线程池,一定要在所有任务执行完成后关闭线程池,确保线程被回收:
public void scheduledTask() { ExecutorService executor = Executors.newFixedThreadPool(3); // 提交任务 executor.submit(task1); executor.submit(task2); executor.submit(task3); // 关闭线程池并等待任务完成 executor.shutdown(); try { // 等待所有任务执行完成,超时则强制关闭 if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); // 恢复当前线程的中断状态 Thread.currentThread().interrupt(); } }
新手提醒
线程是系统级的宝贵资源,线程池本质是线程的容器,创建后如果不主动关闭或复用,会造成线程资源泄漏,最终引发各种资源耗尽的问题。日常开发中,尽量复用线程池,避免频繁创建销毁。
内容的提问来源于stack exchange,提问作者Hoàng Huy
相关产品推荐
相关产品推荐

