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

简介: Java中再一个你可能不经常用,但使用时候不一定会注意的一个知识我们都知道在遇到一些单线程处理很慢的场景,往往我们会采用多线程的方式进行处理,从而缩短处理时间提升性能。

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

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

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();

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

缓存线程: Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

固定线程Executors.newFixedThreadPool(2);

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

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

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 问题,所以说有的时候方便的同时也到来了隐患,作为一个有追求的程序员,我们需要把一些细节的东西完全掌握住,这样在遇到才能在遇到问题的时候宠辱不惊。

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

相关文章
|
数据可视化 数据挖掘
singleCellNet(代码开源)|单细胞层面对细胞分类进行评估,褒贬不一,有胜于无
`singleCellNet`是一款用于单细胞数据分析的R包,主要功能是进行细胞分类评估。它支持多物种和多分组分析,并提供了一个名为`CellNet`的类似工具的示例数据集。用户可以通过安装R包并下载测试数据来运行demo。在demo中,首先加载查询和测试数据,然后训练分类器,接着进行评估,包括查看准确率和召回率的曲线图、分类热图和比例堆积图等。此外,`singleCellNet`还支持跨物种评估,将人类基因映射到小鼠直系同源物进行分析。整体而言,`singleCellNet`是一个用于单细胞分类评估的综合工具,适用于相关领域的研究。
298 6
|
Java Windows
基于java Swing编写扫雷游戏设计实现(已调试)
基于java Swing编写扫雷游戏设计实现(已调试)
185 1
|
Web App开发 应用服务中间件 API
srs搭建rtmp流媒体服务器
srs搭建rtmp流媒体服务器
1810 0
srs搭建rtmp流媒体服务器
【JAVA学习之路 | 提高篇】线程的通信
【JAVA学习之路 | 提高篇】线程的通信
|
存储 监控 安全
会话日志记录
会话日志记录是一种记录计算机程序或通讯工具(如 SecureCRT、Xshell 等)会话过程中的操作、消息和事件的功能。会话日志可以帮助用户回顾之前的操作,排查问题,分析系统行为等。以下是会话日志记录的使用方法:
382 4
|
Cloud Native Java Linux
【云原生 | 16】Docker网络之NetWork网络隔离
每个容器启动时会创建一对虚拟网卡,一个网卡在容器内的虚拟空间内,一个在主机的docker网桥上,然后网卡一连接网卡二,网卡二连接网桥。其他容器的虚拟网卡也都连接到网桥(三层交换)上,就可以使不同的容器之间互相通信了
601 0
【云原生 | 16】Docker网络之NetWork网络隔离
|
前端开发
#yyds干货盘点 歌谣学前端之静态属性
#yyds干货盘点 歌谣学前端之静态属性
165 0
|
Web App开发 安全 iOS开发
Pwn2Own黑客大赛战况:iPhone 20秒被黑
北京时间3月25日上午消息,据国外媒体报道,在网络安全大会CanSecWest周三举行的Pwn2Own黑客大赛中,两名欧洲黑客仅用20秒的时间便成功攻破iPhone,并拿走了1.5万美元奖金。 20秒黑掉iPhone 这两名黑客名为文森佐·埃奥佐(Vincenzo Iozzo)和拉尔夫·温曼(Ralf Weinmann)。
1330 0