前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 中再一个你可能不经常用,但使用的时候不一定会注意的一个知识点

Java 中再一个你可能不经常用,但使用的时候不一定会注意的一个知识点

作者头像
Java极客技术
发布2022-12-02 21:39:43
2480
发布2022-12-02 21:39:43
举报
文章被收录于专栏:Java极客技术Java极客技术

我们都知道在遇到一些单线程处理很慢的场景,往往我们会采用多线程的方式进行处理,从而缩短处理时间提升性能。

往往这个时候我们就会想到 JavaJUC 包中为我们提供的线程池创建方法,如下所示,通过 JDK 自带的 Executors 类中的几个静态方法,创建我们需要的线程池,再通过 ExecutorService 的带返回值的 submit() 或者 不带返回值的 execute() 方式来异步处理业务逻辑。

代码语言:javascript
复制
ExecutorService executorService0 = Executors.newFixedThreadPool(10);
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newCachedThreadPool();
Future<?> submit = executorService0.submit(new Runnable() {
            @Override
            public void run() {

            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {

            }
        });

代码写到了这里,普通程序员都觉得应该已经结束了,剩下的就交给线程池就好了,不过稍微优秀一点的程序员会觉得有点不妥,知道应该再根据我们业务逻辑情况以及服务器的配置,进行线程数的配置,设置合理的线程数。比如这里设置的是 10,我们可以根据自身的情况进行相应的配置。

大部分程序员到这一步就真的觉得应该结束了,该设置的也设置了,接下来就让线程池按照我们的配置好好运行,一切都很完美。

然后事情并没有想象的那么美好,有时候发现在高峰期的时候,偶尔会 OOM 内存溢出的情况,那为什么我们这个逻辑会出现 OOM 呢?

带着这个问题,我们来研究一下,只要看过线程池源码的同学都知道,不管是 Executors.newFixedThreadPool()Executors.newSingleThreadExecutor() 还是 Executors.newCachedThreadPool(),底层的实现都是通过构造 ThreadPoolExecutor 这个类来实现了,不同的地方只是具体参数的不同而已。如下所示

单个线程: Executors.newSingleThreadExecutor();

代码语言:javascript
复制
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

缓存线程: Executors.newCachedThreadPool();

代码语言:javascript
复制
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

固定线程Executors.newFixedThreadPool(2);

代码语言:javascript
复制
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

定时线程: Executors.newScheduledThreadPool(3);(父类中)

代码语言:javascript
复制
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

通过这几个静态方法对应的构造实现,我们可以发现 newSingleThreadExecutor()newFixedThreadPool()中的消息队列的长度为 Integer.MAX_VALUE,而 newCachedThreadPool() 方法允许创建的最大线程数为 Integer.MAX_VALUE

看到这里的小伙伴估计知道我想说什么了,是的,这些固定的静态方法的默认配置有很大的问题!

队列长度太长在高峰期的时候会堆积大量的请求,从而产生 OOM,而创建大量的线程也是一个道理,会把服务器的资源消耗殆尽,从而也产生 OOM!

这一点在阿里巴巴的开发手册中也有明确的说名,如下图所示,可见阿里巴巴手册中的每个点,都值得我们深入的进行钻研!

所以针对这种情况,不建议采用JDK 自带的 Executors 的静态方法,而是通过 ThreadPoolExecutor 类自己来构造线程池,这样每个参数我们都可以根据情况进行自定义。关于 ThreadPoolExecutor 的七个参数的定义如下:

  • corePoolSize: 核心线程数的大小
  • maximumPoolSize: 线程池中允许的最大线程数
  • keepAliveTime: 空闲线程允许的最大的存活时间
  • unit: 存活时间的单位
  • workQueue: 阻塞任务队列
  • threadFactory: 线程工厂用来创建线程
  • handler: 拒绝策略,针对当队列满了时新来任务的处理方式

通过自定义构造函数的具体参数,我们就可以避免上面说到的 OOM 问题,所以说有的时候方便的同时也到来了隐患,作为一个有追求的程序员,我们需要把一些细节的东西完全掌握住,这样在遇到才能在遇到问题的时候宠辱不惊。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java极客技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档