十五、线程池(六)自动创建线程池的弊端
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十五、线程池(六)自动创建线程池的弊端相关的知识,希望对你有一定的参考价值。
参考技术A 所谓的自动创建线程池就是直接调用 Executors 的各种方法来生成常见的线程池,例如 Executors.newCachedThreadPool()。但这样做是有一定风险的,接下来就来逐一分析自动创建线程池可能带来哪些问题。首先来看第一种线程池 FixedThreadPool, 它是线程数量固定的线程池,如源码所示,newFixedThreadPool 内部实际还是调用了 ThreadPoolExecutor 构造函数。
通过往构造函数中传参,创建了一个核心线程数和最大线程数相等的线程池,它们的数量也就是传入的参数,这里的重点是使用的队列是容量没有上限的 LinkedBlockingQueue,如果对任务的处理速度比较慢,那么随着请求的增多,队列中堆积的任务也会越来越多,最终大量堆积的任务会占用大量内存,并发生 OOM ,也就是OutOfMemoryError,这几乎会影响到整个程序,会造成很严重的后果。
第二种线程池是 SingleThreadExecutor,来分析下创建它的源码。
可以看出,newSingleThreadExecutor 和 newFixedThreadPool 的原理是一样的,只不过把核心线程数和最大线程数都直接设置成了 1,但是任务队列仍是无界的 LinkedBlockingQueue,所以也会导致同样的问题,也就是当任务堆积时,可能会占用大量的内存并导致 OOM。
第三种线程池是 CachedThreadPool,创建它的源码下所示。
这里的 CachedThreadPool 和前面两种线程池不一样的地方在于任务队列使用的是 SynchronousQueue,SynchronousQueue 本身并不存储任务,而是对任务直接进行转发,这本身是没有问题的,但会发现构造函数的第二个参数被设置成了 Integer.MAX_VALUE,这个参数的含义是最大线程数,所以由于 CachedThreadPool 并不限制线程的数量,当任务数量特别多的时候,就可能会导致创建非常多的线程,最终超过了操作系统的上限而无法创建新线程,或者导致内存不足。
第四种线程池 ScheduledThreadPool 和第五种线程池 SingleThreadScheduledExecutor 的原理是一样的,创建 ScheduledThreadPool 的源码如下所示。
而这里的 ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的子类,调用的它的构造方法如下所示。
通过源码可以看出,它采用的任务队列是 DelayedWorkQueue,这是一个延迟队列,同时也是一个无界队列,所以和 LinkedBlockingQueue 一样,如果队列中存放过的任务,就可能导致 OOM。
这几种自动创建的线程池都存在风险,相比较而言,自己手动创建会更好,因为我们可以更加明确线程池的运行规则,不仅可以选择适合自己的线程数量,更可以在必要的时候拒绝新任务的提交,避免资源耗尽的风险。
[多线程] 线程池的使用
避免使用 Executors 来创建线程。
说明:
Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
正例1:
// 定时执行线程池
//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
正例2:
//通用线程池
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("demo-pool-%d").build();
ExecutorService pool = new ThreadPoolExecutor(5, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue (1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown(); //gracefully shutdown
正例3:
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
// 请引入一个已经设置过工厂名称的线程工厂对象
//in code userThreadPool.execute(thread);
以上是关于十五、线程池(六)自动创建线程池的弊端的主要内容,如果未能解决你的问题,请参考以下文章