欢迎光临散文网 会员登陆 & 注册

话说java中的线程池

2023-05-10 22:33 作者:涂鸦程序猿  | 我要投稿

一、java中创建线程已经有了new Thread(), implements Runnable, implements Callable等方法了,为什么还需要使用线程池呢?

一般新的事物的产生,肯定是为了弥补旧事物的不足。

那以上几种创建线程的方式有什么不足之处呢?

  1. 每次使用时创建线程,无法控制创建线程的数量,容易创建出过量线程从而消耗完内存。

  2. 创建和销毁线程要耗费时间并造成资源消耗。

那使用线程池创建线程又有什么好处呢?

    不使用线程池创建线程的坏处,恰好是使用线程池的好处。所以使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问 题。

那使用线程池创建线程又有什么坏处呢?

  1. 手动创建线程时,可以创建我们需要的所有线程直到内存不足。而使用线程池时,不是想创建多少线程就能创建多少的,因为定义线程时有最大线程数和队列大小等参数限制。

  2. 占用一定的内存空间,因为线程池定义后,即使不使用,池中的核心线程还是创建了的,只是没有启动而已。

  3. 线程越多,CPU的调度开销越大。

    。。。

这样一看,线程池缺点并不比优点少,那怎样使用才能扬长避短。这就是我们要说的下一个问题,线程池的创建。

二、线程池的创建

java的JUC包中的Executors类为我们提供了以下四种创建线程池的方法:

  • Executors.newSingleThreadExecutor(),单线程线程池

  • Executors.newCachedThreadPool(),缓存线程池

  • Executors.newFixedThreadPool(),固定线程数线程池

  • Executors.newScheduledThreadPool(),周期性线程的线程池

但是以上四种创建线程池的方法不是我们本次的重点,因为阿里java开发手册中强调:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这 样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

原因:Executors 返回的线程池对象的弊端如下: 

1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

好,上面大家可能也看到了,正确的创建线程池的方法是:ThreadPoolExecutor如下图所示,细心的朋友可能已经发现了,其实以上四种创建线程池的方法,内部都是使用的ThreadPoolExecutor,只是每种方法系统帮我们定义了部分参数而已。

由上可知,日常项目开发中我们使用的创建线程池的方法就是下面这句代码:

new ThreadPoolExecutor(核心线程数, 最大线程数, 线程工厂, 存活时间, 存活时间单位, 任务队列, 拒绝策略);

下面对线程线程池创建时的七大参数做个说明。

三、线程池的七大参数

  1. 核心线程数:核心线程大小。可以理解为线程池的基础能力,就是只要线程池创建了,就会有这么多个线程已创建,随时可以获取并启动执行。

  2. 最大线程数:线程池最大容量大小

  3. 任务队列:任务队列,是一个阻塞队列

  4. 存活时间:线程空闲时,能存活多久

  5. 时间单位:时间单位

  6. 线程工厂:线程池中的线程来源于这儿

  7. 拒绝策略:当请求线程数大于队列大小+最大线程数时执行

这儿有个点需要注意:一个线程池能执行的任务数 = 最大线程数 + 任务队列大小,和核心线程数没关系,因为最大线程数涵盖了核心线程数。

这几个参数是怎么相互配合协作的呢?如下。

这儿可能很多人对最大线程数和队列的先后顺序经常搞乱,也就是当核心线程数满了之后,是先进队列排队还是先按最大线程数再起线程执行。下面讲个小例子帮助记忆:

假如当地的电力局缴费大厅,有10个窗口(最大线程数),今天开着4个窗口(核心线程数),然后大厅有30张椅子(任务队列)。那你去缴费的时候,如果4个窗口都有人在缴费了,那你是领个号在椅子上排队等候呢?还是让工作人员给你再开个窗口办理业务,肯定是排队嘛,因为排队对电力局来说没啥损耗,但是再开个窗口确要搭上一个业务员,就是这个道理。


待续未完。。。。。。


话说java中的线程池的评论 (共 条)

分享到微博请遵守国家法律