zoukankan      html  css  js  c++  java
  • 线程池--介绍

    什么是线程池? 为什么要使用它?

    线程池是为了避免线程频繁的创建和销毁带来的性能消耗,而建立的一种池化技术,它是把已创建的线程放入“池”中,当有任务来临时就可以重用已有的线程,无需等待创建的过程,这样就可以有效提高程序的响应速度。

    为什么要使用它?使用线程池有什么好处?

    如果不用线程池,每个任务都创建一个线程会带来问题:

    (1)反复创建线程系统开销比较大,每个线程创建和销毁都需要时间,如果任务比较简单,那么就有可能导致创建和销毁线程消耗的资源比线程执行任务本身消耗的资源还要大。

    (2)过多的线程会占用过多的内存等资源,还会带来过多的上下文切换,同时还会导致系统不稳定。

    我们线程池就是用来平衡线程与系统资源之间的关系。解决问题思路:

    (1)针对反复创建线程开销大的问题,线程池用一些固定的线程(核心线程数corePoolSize)一直保持工作状态并反复执行任务。

    (2)针对过多线程占用太多内存资源的问题,解决思路更直接,线程池会根据需要创建线程,控制线程的总数量,避免占用过多内存资源。

    Executors 提供四种线程池

    1)newCachedThreadPool 是一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute() 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。

    2)newSingleThreadExecutor 创建是一个单线程池,也就是该线程池只有一个线程在工作,所有的任务是串行执行的,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    3)newFixedThreadPool 创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小,线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

    4)newScheduledThreadPool 创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。

    在阿里巴巴的《Java开发手册》中是这样规定线程池的:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的读者更加明确线程池的运行规则,规避资源耗尽的风险。

    ThreadPoolExecutor和ThreadPoolTaskExecutor

    ThreadPoolExecutor这个类是JDK中的线程池类,继承自Executor。

    ThreadPoolTaskExecutor这个类则是spring包下的,是spring为我们提供的线程池类。是Spring对ThreadPoolExecutor的包装,最终还是继承自Executor。

    线程池的参数和工作流程

    详细介绍请查看我的另一篇文章——线程池的参数和工作流程

    线程池的拒绝策略

    当线程池中的任务队列已经被存满,再有任务添加时会先判断当前线程池中的线程数是否大于等于线程池的最大值,如果是,则会触发线程池的拒绝策略。

    Java自带的拒绝策略有4种:

    (1) AbortPolicy,终止策略,线程池会抛出异常并终止执行,它是默认的拒绝策略;

    (2) CallerRunsPolicy,把任务交给当前线程来执行;

    (3) DiscardPolicy,忽略此任务(最新的任务);

    (4) DiscardOldestPolicy,忽略最早的任务(最先加入队列的任务)。

    拒绝策略AbortPolicy示例

    可以看出当第6个任务来的时候,线程池则执行了AbortPolicy拒绝策略,抛出了异常。因为队列最多存储2个任务,最大可以创建3个线程来执行任务(2+3=5),所以当第 6 个任务来的时候,此线程池就“忙”不过来了。

    项目中使用的拒绝策略是哪个?

    拒绝策略是 CallerRunsPolicy,当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。这样做主要有两点好处。

    第一点新提交的任务不会被丢弃,这样也就不会造成业务损失。

    第二点好处是,由于谁提交任务谁就要负责执行任务,这样提交任务的线程就得负责执行任务,而执行任务又是比较耗时的,在这段期间,提交任务的线程被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于是一个负反馈。在此期间,线程池中的线程也可以充分利用这段时间来执行掉一部分任务,腾出一定的空间,相当于是给了线程池一定的缓冲期。

    线程工厂源码

    Ctrl点进去Executors.defaultThreadFactory()后

     Ctrl点进去new DefaultThreadFactory()  

     补充说明: 我们也可以自定义一个线程工厂,通过实现 ThreadFactory 接口来完成,这样就可以自定义线程的名称或线程执行的优先级了。

    实战

    sdh中使用spring为我们提供的线程池类——ThreadPoolTaskExecutor

    希望本文章对您有帮助,您的转发、点赞是我的创作动力,十分感谢。更多好文推荐,请关注我的微信公众号--JustJavaIt
  • 相关阅读:
    Centos服务器搭建(3)——安装maven
    Centos服务器搭建(2)——安装tomcat
    Centos服务器搭建(1)——安装jdk
    mysql主从复制
    Json中返回换行符处理
    github pages 绑定域名
    SharePoint学习笔记——子页面
    SharePoint学习笔记——母版页
    SSH+Oracle的整合(SSH与Oracle整合坑巨多)
    SSH整合做CRUD(大神老师整理)
  • 原文地址:https://www.cnblogs.com/liaowenhui/p/15145635.html
Copyright © 2011-2022 走看看