美文网首页
如何根据需要定制Java线程池

如何根据需要定制Java线程池

作者: 明翼 | 来源:发表于2022-02-09 14:56 被阅读0次

默认的线程池使用起来有不少的风险,比如容易OOM,所以一般我们不使用系统的默认的线程池,需要自己根据业务特点来定制线程池。
定制线程池无非是关注线程池的几个参数

一 核心线程数

核心线程数即corePoolSize ,如果是cpu密集的任务,核心线程数设置为cpu核心的1-2倍,如果是io密集的任务,设置的线程数为2倍以上,具体可以根据公式评估下:
《Java并发编程实战》的作者 Brain Goetz 推荐的计算方法:
线程数 = CPU 核心数 *(1+平均等待时间/平均工作时间)
通过这个公式,如果我们任务的等待时间比较长(IO密集型),那么线程数就多,如果平均工作时间比较长(CPU密集型),那线程数就少。
线程数少了无法充分利用系统资源,线程数多了调度频繁,上下文切换比较多,反而会降低性能,具体设置多少,我们还可以通过监控JVM线程数和cpu的负载情况,来设定合适的线程数。

最大线程数一般设置为核心线程数的几倍,以便可以达到更好的应对突发的情况。

二 阻塞队列

阻塞队列我们可以选择java线程池里面常用的几个队列:LinkedBlockingQueue 或者 SynchronousQueue 或者 DelayedWorkQueue。不过更推荐使用ArrayBlockingQueue,这个队列内部是通过数组来实现的,它最大的特点是有容量限制,一旦设置了不可以修改,如果线程池的线程数量已经达到了最大线程数,且队列也满了,后续的任务都会按照拒绝策略进行拒绝了,但是对于无限制增加线程数或者队列容量不限制的场景,ArrayBlockingQueue队列加上限制了最大线程数据,防止了程序占用内存太多,而导致的OOM问题。

如果我们使用容量更大的队列和更小的最大线程数,就可以减少上下文切换带来的开销,但也可能因此降低整体的吞吐量;如果我们的任务是 IO 密集型,则可以选择稍小容量的队列和更大的最大线程数,这样整体的效率就会更高,不过也会带来更多的上下文切换。

三 线程工厂

定制线程工厂的目的是为了让线程名变的可以识别,可以根据任务名称来设置线程名。
比如可以通过com.google.common.util.concurrent.ThreadFactory
Builder 来实现,如代码所示。

ThreadFactoryBuilder builder = new ThreadFactoryBuilder();
ThreadFactory calFactory = builder.setNameFormat("cal-pool-%d").build();

这样生成的线程名称为: cal-pool-1,cal-pool-2等

四 拒绝策略

默认的实现的四种策略AbortPolicy,DiscardPolicy,DiscardOldestPolicy 或者 CallerRunsPolicy 我们一般会选择CallerRunsPolicy 策略或AbortPolicy更合适点,还可以通过实现RejectedExecutionHandler 接口来实现自己的拒绝策略 ,如下:

private static class CustomMyRejectionHandler implements RejectedExecutionHandler { 

    @Override

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 

 // 打印 临时存储啊
    } 

}

我们通过实现rejectedExecution这个接口来自定义拒绝策略。

相关文章

网友评论

      本文标题:如何根据需要定制Java线程池

      本文链接:https://www.haomeiwen.com/subject/ngvwkrtx.html