threadpoolexecutorexecutor="tomcatthreadpool"什么意思

threadpoolexecutor  时间:2021-06-25  阅读:()

java线程池怎么实现

要想理解清楚java线程池实现原理,明白下面几个问题就可以了: (1):线程池存在哪些状态,这些状态之间是如何进行切换的呢? (2):线程池的种类有哪些? (3):创建线程池需要哪些参数,这些参数的具体含义是什么? (4):将任务添加到线程池之后运行流程? (5):线程池是怎么做到重用线程的呢? (6):线程池的关闭 首先回答第一个问题:线程池存在哪些状态; 查看ThreadPoolExecutor源码便知晓: [java]?view plain?copy
  • //?runState?is?stored?in?the?high-order?bits??
  • private?static?final?int?RUNNING????=?-1?<<?COUNT_BITS;??
  • private?static?final?int?SHUTDOWN???=??0?<<?COUNT_BITS;??
  • private?static?final?int?STOP???????=??1?<<?COUNT_BITS;??
  • private?static?final?int?TIDYING????=??2?<<?COUNT_BITS;??
  • private?static?final?int?TERMINATED?=??3?<<?COUNT_BITS;?? 存在5种状态: <1>Running:可以接受新任务,同时也可以处理阻塞队列里面的任务; <2>Shutdown:不可以接受新任务,但是可以处理阻塞队列里面的任务; <3>:不可以接受新任务,也不处理阻塞队列里面的任务,同时还中断正在处理的任务; <4>Tidying:属于过渡阶段,在这个阶段表示所有的任务已经执行结束了,当前线程池中是不存在有效的线程的,并且将要调用terminated方法; <5>Terminated:终止状态,这个状态是在调用完terminated方法之后所处的状态; 那么这5种状态之间是如何进行转换的呢?查看ThreadPoolExecutor源码里面的注释便可以知道啦: [java]?view plain?copy
  • *?RUNNING?->?SHUTDOWN??
  • *????On?invocation?of?shutdown(),?perhaps?implicitly?in?finalize()??
  • *?(RUNNING?or?SHUTDOWN)?->?STOP??
  • *????On?invocation?of?shutdownNow()??
  • *?SHUTDOWN?->?TIDYING??
  • *????When?both?queue?and?pool?are?empty??
  • *?STOP?->?TIDYING??
  • *????When?pool?is?empty??
  • *?TIDYING?->?TERMINATED??
  • *????When?the?terminated()?hook?method?pleted?? 从上面可以看到,在调用shutdown方法的时候,线程池状态会从Running转换成Shutdown;在调用shutdownNow方法的时候,线程池状态会从Running/Shutdown转换成;在阻塞队列为空同时线程池为空的情况下,线程池状态会从Shutdown转换成Tidying;在线程池为空的情况下,线程池状态会从转换成Tidying;当调用terminated方法之后,线程池状态会从Tidying转换成Terminate; 在明白了线程池的各个状态以及状态之间是怎么进行切换之后,我们来看看第二个问题,线程池的种类: (1):CachedThreadPool:缓存线程池,该类线程池中线程的数量是不确定的,理论上可以达到Integer.MAX_VALUE个,这种线程池中的线程都是非核心线程,既然是非核心线程,那么就存在超时淘汰机制了,当里面的某个线程空闲时间超过了设定的超时时间的话,就会回收掉该线程; (2):FixedThreadPool:固定线程池,这类线程池中是只存在核心线程的,对于核心线程来说,如果我们不设置allowCoreThreadTimeOut属性的话是不存在超时淘汰机制的,这类线程池中的corePoolSize的大小是等于maximumPoolSize大小的,也就是说,如果线程池中的线程都处于活动状态的话,如果有新任务到来,他是不会开辟新的工作线程来处理这些任务的,只能将这些任务放到阻塞队列里面进行等到,直到有核心线程空闲为止; (3):ScheduledThreadPool:任务线程池,这种线程池中核心线程的数量是固定的,而对于非核心线程的数量是不限制的,同时对于非核心线程是存在超时淘汰机制的,主要适用于执行定时任务或者周期性任务的场景; (4):SingleThreadPool:单一线程池,线程池里面只有一个线程,同时也不存在非核心线程,感觉像是FixedThreadPool的特殊版本,他主要用于确保任务在同一线程中的顺序执行,有点类似于进行同步吧; 接下来我们来看第三个问题,创建线程池需要哪些参数: 同样查看ThreadPoolExecutor源码,查看创建线程池的构造函数: [java]?view plain?copy
  • public?ThreadPoolExecutor(int?corePoolSize,??
  • int?maximumPoolSize,??
  • long?keepAliveTime,??
  • TimeUnit?unit,??
  • BlockingQueue<Runnable>?workQueue,??
  • ThreadFactory?threadFactory,??
  • RejectedExecutionHandler?handler)?? 不管你调用的是ThreadPoolExecutor的哪个构造函数,最终都会执行到这个构造函数的,这个构造函数有7个参数,正是由于对这7个参数值的赋值不同,造成生成不同类型的线程池,比如我们常见的CachedThreadPoolExecutor、FixedThreadPoolExecutor SingleThreadPoolExecutor、ScheduledThreadPoolExecutor,我们老看看这几个参数的具体含义: <1>corePoolSize:线程池中核心线程的数量;当提交一个任务到线程池的时候,线程池会创建一个线程来执行执行任务,即使有其他空闲的线程存在,直到线程数达到corePoolSize时不再创建,这时候会把提交的新任务放入到阻塞队列中,如果调用了线程池的preStartAllCoreThreads方法,则会在创建线程池的时候初始化出来核心线程; <2>maximumPoolSize:线程池允许创建的最大线程数;如果阻塞队列已经满了,同时已经创建的线程数小于最大线程数的话,那么会创建新的线程来处理阻塞队列中的任务; <3>keepAliveTime:线程活动保持时间,指的是工作线程空闲之后继续存活的时间,默认情况下,这个参数只有线程数大于corePoolSize的时候才会起作用,即当线程池中的线程数目大于corePoolSize的时候,如果某一个线程的空闲时间达到keepAliveTime,那么这个线程是会被终止的,直到线程池中的线程数目不大于corePoolSize;如果调用allowCoreThreadTimeOut的话,在线程池中线程数量不大于corePoolSize的时候,keepAliveTime参数也可以起作用的,知道线程数目为0为止; <4>unit:参数keepAliveTime的时间单位; <5>workQueue:阻塞队列;用于存储等待执行的任务,有四种阻塞队列类型,ArrayBlockingQueue(基于数组的有界阻塞队列)、LinkedBlockingQueue(基于链表结构的阻塞队列)、SynchronousQueue(不存储元素的阻塞队列)、PriorityBlockingQueue(具有优先级的阻塞队列); <6>threadFactory:用于创建线程的线程工厂; <7>handler:当阻塞队列满了,且没有空闲线程的情况下,也就是说这个时候,线程池中的线程数目已经达到了最大线程数量,处于饱和状态,那么必须采取一种策略来处理新提交的任务,我们可以自己定义处理策略,也可以使用系统已经提供给我们的策略,先来看看系统为我们提供的4种策略,AbortPolicy(直接抛出异常)、CallerRunsPolicy(只有调用者所在的线程来运行任务)、DiscardOldestPolicy(丢弃阻塞队列中最近的一个任务,并执行当前任务)、Discard(直接丢弃); 接下来就是将任务添加到线程池之后的运行流程了; 我们可以调用submit或者execute方法,两者最大的区别在于,调用submit方法的话,我们可以传入一个实现Callable接口的对象,进而能在当前任务执行结束之后通过Future对象获得任务的返回值,submit内部实际上还是执行的execute方法;而调用execute方法的话,是不能获得任务执行结束之后的返回值的;此外,调用submit方法的话是可以抛出异常的,但是调用execute方法的话,异常在其内部得到了消化,也就是说异常在其内部得到了处理,不会向外传递的; 因为submit方法最终也是会执行execute方法的,因此我们只需要了解execute方法就可以了: 在execute方法内部会分三种情况来进行处理: <1>:首先判断当前线程池中的线程数量是否小于corePoolSize,如果小于的话,则直接通过addWorker方法创建一个新的Worker对象来执行我们当前的任务; <2>:如果说当前线程池中的线程数量大于corePoolSize的话,那么会尝试将当前任务添加到阻塞队列中,然后第二次检查线程池的状态,如果线程池不在Running状态的话,会将刚刚添加到阻塞队列中的任务移出,同时拒绝当前任务请求;如果第二次检查发现当前线程池处于Running状态的话,那么会查看当前线程池中的工作线程数量是否为0,如果为0的话,就会通过addWorker方法创建一个Worker对象出来处理阻塞队列中的任务; <3>:如果原先线程池就不处于Running状态或者我们刚刚将当前任务添加到阻塞队列的时候出现错误的话,那么会去尝试通过addWorker创建新的Worker来处理当前任务,如果添加失败的话,则拒绝当前任务请求; 可以看到在上面的execute方法中,我们仅仅只是检查了当前线程池中的线程数量有没有超过corePoolSize的情况,那么当前线程池中的线程数量有没有超过maximumPoolSize是在哪里检测的呢?实际上是在addWorker方法里面了,我们可以看下addWorker里面的一段代码: [java]?view plain?copy
  • if?(wc?>=?CAPACITY?||??
  • wc?>=?(core???corePoolSize?:?maximumPoolSize))??
  • return?false;?? 如果当前线程数量超过maximumPoolSize的话,直接就会调用return方法,返回false; 其实到这里我们很明显可以知道,一个线程池中线程的数量实际上就是这个线程池中Worker的数量,如果Worker的大小超过了corePoolSize,那么任务都在阻塞队列里面了,Worker是Java对我们任务的一个封装类,他的声明是酱紫的: [java]?view plain?copy
  • private?final?class?Worker??
  • extends?AbstractQueuedSynchronizer??
  • implements?Runnable?? 可以看到他实现了Runnable接口,他是在addWorker方法里面通过new Worker(firstTask)创建的,我们来看看他的构造函数就知道了: [java]?view plain?copy
  • Worker(Runnable?firstTask)?{??
  • setState(-1);?//?inhibit?interrupts?until?runWorker??
  • this.firstTask?=?firstTask;??
  • this.thread?=?getThreadFactory().newThread(this);??
  • }?? 而这里的firstTask其实就是我们调用execute或者submit的时候传入的那个参数罢了,一般来说这些参数是实现Callable或者Runnable接口的; 在通过addWorker方法创建出来Worker对象之后,这个方法的最后会执行Worker内部thread属性的start方法,而这个thread属性实际上就是封装了Worker的Thread,执行他的start方法实际上执行的是Worker的run方法,因为Worker是实现了Runnable接口的,在run方法里面就会执行runWorker方法,而runWorker方法里面首先会判断当前我们传入的任务是否为空,不为空的话直接就会执行我们通过execute或者submit方法提交的任务啦,注意一点就是我们虽然会通过submit方法提交实现了Callable接口的对象,但是在调用submit方法的时候,其实是会将Callable对象封装成实现了Runnable接口对象的,不信我们看看submit方法源码是怎么实现的: [java]?view plain?copy
  • public?<T>?Future<T>?submit(Callable<T>?task)?{??
  • if?(task?==?null)?throw?new?NullPointerException();??
  • RunnableFuture<T>?ftask?=?newTaskFor(task);??
  • execute(ftask);??
  • return?ftask;??
  • }?? 看到没有呢,实际上在你传入实现了Callable接口对象的时候,在submit方法里面是会将其封装成RunnableFuture对象的,而RunnableFuture接口是继承了Runnable接口的;那么说白了其实就是直接执行我们提交任务的run方法了;如果为空的话,则会通过getTask方法从阻塞队列里面拿出一个任务去执行;在任务执行结束之后继续从阻塞队列里面拿任务,直到getTask的返回值为空则退出runWorker内部循环,那么什么情况下getTask返回为空呢?查看getTask方法的源码注释可以知道:在Worker必须需要退出的情况下getTask会返回空,具体什么情况下Worker会退出呢?(1):当Worker的数量超过maximumPoolSize的时候;(2):当线程池状态为的时候;(3):当线程池状态为Shutdown并且阻塞队列为空的时候;(4):使用等待超时时间从阻塞队列中拿数据,但是超时之后仍然没有拿到数据; 如果runWorker方法退出了它里面的循环,那么就说明当前阻塞队列里面是没有任务可以执行的了,你可以看到在runWorker方法内部的finally语句块中执行了processWorkerExit方法,用来对Worker对象进行回收操作,这个方法会传入一个参数表示需要删除的Worker对象;在进行Worker回收的时候会调用tryTerminate方法来尝试关闭线程池,在tryTerminate方法里面会检查是否有Worker在工作,检查线程池的状态,没问题的话就会将当前线程池的状态过渡到Tidying,之后调用terminated方法,将线程池状态更新到Terminated; 从上面的分析中,我们可以看出线程池运行的4个阶段: (1):poolSize < corePoolSize,则直接创建新的线程(核心线程)来执行当前提交的任务; (2):poolSize = corePoolSize,并且此时阻塞队列没有满,那么会将当前任务添加到阻塞队列中,如果此时存在工作线程(非核心线程)的话,那么会由工作线程来处理该阻塞队列中的任务,如果此时工作线程数量为0的话,那么会创建一个工作线程(非核心线程)出来; (3):poolSize = corePoolSize,并且此时阻塞队列已经满了,那么会直接创建新的工作线程(非核心线程)来处理阻塞队列中的任务; (4):poolSize = maximumPoolSize,并且此时阻塞队列也满了的话,那么会触发拒绝机制,具体决绝策略采用的是什么就要看我们创建ThreadPoolExecutor的时候传入的RejectExecutionHandler参数了; 接下来就是线程池是怎么做到重用线程的呢? 个人认为线程池里面重用线程的工作是在getTask里面实现的,在getTask里面是存在两个for死循环嵌套的,他会不断的从阻塞对列里面取出需要执行的任务,返回给我们的runWorker方法里面,而在runWorker方法里面只要getTask返回的任务不是空就会执行该任务的run方法来处理它,这样一直执行下去,直到getTask返回空为止,此时的情况就是阻塞队列里面没有任务了,这样一个线程处理完一个任务之后接着再处理阻塞队列中的另一个任务,当然在线程池中的不同线程是可以并发处理阻塞队列中的任务的,最后在阻塞队列内部不存在任务的时候会去判断是否需要回收Worker对象,其实Worker对象的个数就是线程池中线程的个数,至于什么情况才需要回收,上面已经说了,就是四种情况了; 最后就是线程池是怎样被关闭的呢? 涉及到线程池的关闭,需要用到两个方法,shutdown和shutdownNow,他们都是位于ThreadPoolExecutor里面的,对于shutdown的话,他会将线程池状态切换成Shutdown,此时是不会影响对阻塞队列中任务执行的,但是会拒绝执行新加进来的任务,同时会回收闲置的Worker;而shutdownNow方法会将线程池状态切换成,此时既不会再去处理阻塞队列里面的任务,也不会去处理新加进来的任务,同时会回收所有Worker;

    用了线程池Executors怎么join不好用了

    public static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

    对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。

    调用 execute 将重用以前构造的线程

    executor="tomcatthreadpool"什么意思

    executor="tom cat thread pool" 执行器=“汤姆猫线程池” 双语对照 例句: 1. Today, most server applications use mon worker queue and thread pool to exploitthe concurrency provided by the underlying hardware. 目前,大部分的服务器应用都使用一个共用的工作队列和一个线程池来利用底层的硬件提供的并发性。

  • 无忧云(25元/月),国内BGP高防云服务器 2核2G5M

    无忧云官网无忧云怎么样 无忧云服务器好不好 无忧云值不值得购买 无忧云,无忧云是一家成立于2017年的老牌商家旗下的服务器销售品牌,现由深圳市云上无忧网络科技有限公司运营,是正规持证IDC/ISP/IRCS商家,主要销售国内、中国香港、国外服务器产品,线路有腾讯云国外线路、自营香港CN2线路等,都是中国大陆直连线路,非常适合免北岸建站业务需求和各种负载较高的项目,同时国内服务器也有多个BGP以及高...

    3C云1核1G 9.9元 4核4G 16元 美国Cera 2核4G 24元

    3C云互联怎么样?3C云互联专注免备案香港美国日本韩国台湾云主机vps服务器,美国高防CN2GIA,香港CN2GIA,顶级线路优化,高端品质售后无忧!致力于对互联网云计算科技深入研发与运营的极客共同搭建而成,将云计算与网络核心技术转化为最稳定,安全,高速以及极具性价比的云服务器等产品提供给用户!专注为个人开发者用户,中小型,大型企业用户提供一站式核心网络云端服务部署,促使用户云端部署化简为零,轻松...

    RackNerd 2022春节促销提供三款年付套餐 低至年付10.88美元

    RackNerd 商家我们应该是比较熟悉的商家,速度一般,但是人家便宜且可选机房也是比较多的,较多集中在美国机房。包括前面的新年元旦促销的时候有提供年付10美元左右的方案,实际上RackNerd商家的营销策略也是如此,每逢节日都有活动,配置简单变化,价格基本差不多,所以我们网友看到没有必要囤货,有需要就选择。RackNerd 商家这次2022农历新年也是有几款年付套餐。低至RackNerd VPS...

    threadpoolexecutor为你推荐
    activity跳转android中从一个activity跳转到另一个activity的详细步骤dnf装备代码dnf超级装备 ,极品代码是什么意思?求教!爱短信官网求助。爱短信爱聊怎么用?一起作业网站一起作业官网是什么?云输入法QQ云输入法怎样进行中英文切换?erp系统教程在一个公司里怎样去学ERPgbk内码怎么查GBK内码?怎么查微信注册时间怎么查对方的微信号什么时候注册的怎么查微信注册时间怎么知道微信上次登录时间在线沟通有效沟通的六个要点
    天津虚拟主机 猫咪永久域名收藏地址 域名信息查询 ip反查域名 vps服务器 科迈动态域名 草根过期域名 hostgator kvmla typecho 华为网络硬盘 cdn联盟 最漂亮的qq空间 空间首页登陆 主机管理系统 免费个人主页 腾讯数据库 阿里云个人邮箱 国外代理服务器 免费赚q币 更多