线程池ThreadPool

1、什么是线程池?

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

2、为什么要用线程池?

频繁创建线程,然后重复的从就绪到运行状态,效率非常低。
采用线程池直接实现复用,不用频繁创建,也不用等待CPU的调度,效率非常高。

3、线程池的作用

        第一:降低资源消耗。通过复用已创建的线程,可以降低线程创建和销毁造成的资源消耗。
  第二:提高响应速度(提高效率)。线程池中的线程不会阻塞、死亡,一直处于运行状态,当任务到达时,任务可以不需要等待线程的创建和CPU调度就可以立即执行任务。
  第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
  第四:提供更强大的功能:线程具备可扩展性,允许开发人员向其中增加更多的的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行

  缺点:比较消耗CPU

4 、线程池的创建方式(官方推荐自定义)

  阿里巴巴开发手册中,强制要求线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式可以规避资源耗尽的风险。因为
  因为Executors已封装好的几个线程池,底层都是基于无界队列实现,缓存的线程队列可以无限的存放容量大小,有可能耗尽内存资源,导致内存溢出。

5、为什么阿里官方推荐基于ThreadPoolExecutor自定义线程池?

  1)因为已封装好的几个线程池,底层都是基于无界队列实现,缓存的线程队列可以无限的存放容量大小,有可能会内存溢出。
  2)无界队列,会造成线程池中的最大线程池数无效。 

6、构造函数参数解析

        corePoolSize: 核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
  maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程;队列满的时候会创建新的线程。
  keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。因为线程一直处于运行状态非常消耗CPU。
  unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
  workQueue:工作队列,先进先出。队列底层,有基于数组实现,也有基于链表实现,如果查询频繁,使用数组;如果修改频繁,使用链表。
    阻塞队列:从队列中取出元素的时候,如果队列中没有元素,会进行指定时间的等待,如果任然没有新的元素进来,则返回null。
              往队列中添加元素的时候,如果队列已满,会进行指定时间的等待,如果任然没有元素被取出,则返回false。
  handler:  自定义封装拒绝策略回调方法。 

7、线程池底层原理分析

  执行任务的线程数 < 核心线程数时,新任务直接复用线程池中的线程;
  执行任务的线程数 ≥ 核心线程数时,新任务存放在缓存队列中;
  执行任务的线程数 > 核心线程数,且缓存队列满时,创建新线程执行任务;
  执行任务的线程数 > 最大线程数,且缓存队列满时,对任务采取进拒绝策略。

  1)提交任务的时候比较核心线程数,如果当前执行任务线程数量小于核心线程数,则直接复用线程执行。
  2)如果执行任务的线程数量等于核心线程数,则缓存到队列中。
  3)如果缓存队列满了,且执行任务的线程数小于最大线程数,则创建线程执行。
  4)如果队列已满且执行任务的线程数等于最大线程数,则走拒绝策略。
  注意:最大线程数=核心线程数+非核心线程数。如果非核心线程数在一定时间没有执行任务,为了避免浪费内存,线程会被销毁。

8、缓存队列满了,如何拒绝任务?

  如果队列满了,且执行任务的线程数>最大线程数,则当前线程走拒绝策略。可以自定义异拒绝异常。线程池会调用rejectedExecutionHandler来处理这个任务。

 1)AbortPolicy 丢弃任务,抛运行时异常(默认)。如果没有设置,默认是AbortPolicy,会抛出异常。可以采用异常捕获,将拒绝的线程任务记录到日志表中,后期做定时任务补偿。

  2)CallerRunsPolicy 执行任务。用main方法执行被拒绝的线程。

  3)DiscardPolicy 忽视,什么都不会发生
  4)DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
  5)实现RejectedExecutionHandler接口,可自定义处理器.

 9、合理配置线程池

1)CPU密集型

  CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。
  CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程,该任务都不可能得到加速,因为CPU总的运算能力就那些。

2)IO密集

  IO密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以在IO密集型任务中使用多线程可以大大的加速程序运行,即时在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间。

3)配置

  ① CPU密集型任务应配置尽可能小的线程,如配置CPU个数+1的线程数;
  ② IO密集型任务应配置尽可能多的线程,因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,如配置两倍CPU个数+1;
  ③ 对于混合型的任务,如果可以拆分,拆分成IO密集型和CPU密集型分别处理,前提是两者运行的时间是差不多的,如果处理时间相差很大,则没必要拆分了。

4)最佳线程数

  最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
  比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:
    最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

5)总结

  线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

  以上公式与之前的CPU和IO密集型任务设置线程数基本吻合。

  CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务

  IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是引用中的代码示例: ```java ExecutorService executor = Executors.newFixedThreadPool(2); Runnable worker = new Runnable() { public void run() { // do some work } }; executor.execute(worker); ``` 这段代码创建了一个固定大小的线程池,其中corePoolSize为2,maximumPoolSize也为2。这意味着线程池最多会保持2个线程处于活动状态,并在任务到达时创建新的线程来处理任务。当队列中没有任务时,线程池中的线程会进入空闲状态,直到有新的任务到来。如果使用的是无界队列(例如LinkedBlockingQueue),那么线程数就不会超过corePoolSize。keepAliveTime参数指定了线程池中的空闲线程在等待新任务时的存活时间。 以下是引用中的代码示例: ```java ExecutorService executor = Executors.newFixedThreadPool(5); Runnable worker = new Runnable() { public void run() { // do some work } }; executor.execute(worker); ``` 这段代码创建了一个固定大小的线程池,其中corePoolSize为5,maximumPoolSize也为5。这意味着线程池最多会保持5个线程处于活动状态,并在任务到达时创建新的线程来处理任务。当队列中没有任务时,线程池中的线程会进入空闲状态,直到有新的任务到来。如果使用的是无界队列(例如LinkedBlockingQueue),那么线程数就不会超过corePoolSize。keepAliveTime参数指定了线程池中的空闲线程在等待新任务时的存活时间。这段代码创建了一个5个线程的线程池,如果需要创建更多的线程,可以调整corePoolSize和maximumPoolSize的值。如果使用的是有界队列(例如ArrayBlockingQueue),那么可以通过调用put()方法将任务放入队列中。这段代码展示了如何创建一个固定大小的线程池,并使用execute()方法执行任务。如果需要停止线程池,可以使用shutdown()方法或者shutdownNow()方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [线程池ThreadPool详解](https://blog.csdn.net/qq_43651945/article/details/124717896)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值