线程池创建及如何避免OOM

常见的线程池创建方式。
创建线程池的方式主要分为两种,自动创建和手动创建:
a、通过 Executors 执行器自动创建线程池。
b、通过 ThreadPoolExecutor 手动创建线程池。
在这里插入图片描述
Executors:
Executors创建线程池很便捷,常用的创建方式有6种:
1、newSingleThreadExecutor() 创建一个单线程化的线程池;
2、newFixedThreadPool() 创建固定线程数量的线程池;
3、newCachedThreadPool() 创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
4、newScheduledThreadPool() 创建一个可以延迟或定期执行任务的线程池;
5、newSingleThreadScheduledExecutor()创建一个单线程执行器,该执行器可以安排命令在给定延迟后运行,或定期执行;
6、newWorkStealingPool() 使用可用处理器数量作为其目标并行度级别创建工作窃取线程池 [ JDK1.8新增]。

Executors提供了这么多创建方法,功能还是很强大的,又方便又简洁,我们直接用不就好了吗!但通常不建议使用这个类来创建线程池,因为很有可能造成资源耗尽的风险。

OOM源头所在!
既然我们知道Executors创建的线程池存在OOM的风险,那么我们深入Executors的源码来分析一下到底是什么原因导致的呢?
以newFixedThreadPool为例:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                     0L, TimeUnit.MILLISECONDS,
                     new LinkedBlockingQueue<Runnable>());
    }

可发现,创建线程池所用的阻塞队列是LinkedBlockingQueue。而再看看LinkedBlockingQueue的构造函数,我们能看到默认构造出来的是一个容量为Integer.MAX_VALUE的队列。

public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

所以当大量的线程在创建的时候,线程池处理不过来,就会加入到等待队列中,而默认创建的等待队列是一个容量为Integer.MAX_VALUE的无边界队列,从而就很有可能在服务器资源紧缺的情况下因为任务过多而导致内存溢出问题。

上面的问题是FixedThreadPool 和 SingleThreadPool会出现的情况;
但是CacheThreadPool和ScheduledThreadPool也存在类似问题。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
    
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
              60L, TimeUnit.SECONDS,
              new SynchronousQueue<Runnable>());
    }

从源码可以看出这两种情况都将最大线程数设置成Integer.MAX_VALUE,同样也会造成OOM。

ThreadPoolExecutor:
上面的Executors创建线程池的方法内部都是调用ThreadPoolExecutor,只不过屏蔽了创建的参数。
所以我们应自己使用ThreadPoolExecutor来进行线程池的创建。这样才能让我们更加明确线程池的运行规则,从而规避系统资源耗尽的情况发生。
ThreadPoolExecutor构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

corePoolSize :线程池中核心线程数,即使线程处于空闲状态,也要保留在线程池中的线程数;
maximumPoolSize :线程池中允许的最大线程数;
keepAliveTime :当线程数大于核心线程数时,这是多余的空闲线程在终止前等待新任务的最大时间;
unit :keepAliveTime参数的时间单位;
workQueue :在任务执行之前用于保存任务的队列;
threadFactory :当执行程序创建一个新的线程处理程序时使用的工厂;
handler :拒绝策略,当执行因为线程边界和队列容量达到而阻塞时使用的处理程序。

总结:
本文给大家分析了Excutors导致OOM出现的原因,也为大家介绍了使用ThreadPoolExcutor手动创建线程池时每个参数的具体含义,希望在实际开发中根据具体情况,创建合适的线程池,避免OOM的发生。

  • 25
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ghx3110

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值