线程池

1.定义

线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。
如果每个请求都创建一个线程去处理,那么服务器的资源很快就会被耗尽,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
作用:能够极大地减少某些线程的频繁创建和销毁带来的时间开销

比如说项目中使用到了thrift,client会向server发起大量并发请求,如果每次请求都去建立一个线程,维持tcp连接,那无疑开销是巨大的,所以在客户端自己实现了一个thrift连接池。

2.线程池的实现原理

2.1 线程池的关键参数:

  • corePoolSize就是线程池中的核心线程数量,这几个核心线程,只是在没有用的时候,也不会被回收

  • maximumPoolSize就是线程池中可以容纳的最大线程的数量

  • keepAliveTime,就是线程池中除了核心线程之外的其他的最长可以保留的时间,因为在线程池中,除了核心线程即使在无任务的情况下也不能被清 除,其余的都是有存活时间的,意思就是非核心线程可以保留的最长的空闲时间,

  • util,就是计算这个时间的一个单位。

  • workQueue,就是等待队列,任务可以储存在任务队列中等待被执行,执行的是FIFIO原则(先进先出)。

  • threadFactory,就是创建线程的线程工厂。

  • handler,是一种拒绝策略,我们可以在任务满了之后,拒绝执行某些任务。

2.2 线程池的常见形式

  1. 单线程池——newSingleThreadExecutor:线程池只维持一个活跃线程,它只会用唯一的工作线程来执行任务。
  2. 固定大小线程池——newFixedThreadPool:设置一个固定大小的队列,以及指定核心线程数和最大线程数,可控制线程最大并发数,超出的线程会在队列中等待,如果,队列已满但还未到最大线程数,则会继续开辟线程处理该入队失败的请求,如果已到最大线程数,则采取拒绝策略。
    默认是核心线程数等于最大线程数。其阻塞队列采取链表类阻塞队列(LinkedBlockingQueue)。
  3. 可缓存线程池——newCachedThreadPool:核心线程数为0,最大线程数为Int.MaxInteger,阻塞队列采取同步队列(SynchronousQueue),同步队列也是阻塞队列的一种,但是其特点在于该队列每次的插入,都必须要对应一个删除操作才能继续。相当于是提交的任务不会真实保存,只会马上开启新的线程进行处理。如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  4. newScheduledThreadPool
    创建一个定长线程池,支持定时及周期性任务执行。

2.3 线程池中使用有限的阻塞队列和无限的阻塞队列的区别
有限阻塞队列会存在任务入队失败的情况,而无限阻塞队列则不会,他会一直往队列中添加请求,直到系统资源耗尽。

3.如何合理设置线程池队列长度

tomcat、Dubbo 等业界成熟的产品是如何设置线程队列,我们主要分析下这两个中间件

1.JDK线程池策略

  1. 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  2. 如果此时线程池中的数量大于等于corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
  3. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理添加的任务。
  4. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过
    handler所指定的策略来处理此任务。也就是处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
  5. 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

2.Tomcat线程池策略

Tomcat的线程池队列是无限长度的,但是线程池会一直创建到maximumPoolSize,然后才把请求放入等待队列中

tomcat 任务队列org.apache.tomcat.util.threads.TaskQueue其继承与LinkedBlockingQueue,覆写offer方法。

@Override
    public boolean offer(Runnable o) {
      //we can't do any checks
        if (parent==null) return super.offer(o);
        //we are maxed out on threads, simply queue the object
        if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
        //we have idle threads, just add it to the queue
        if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
         //线程个数小于MaximumPoolSize会创建新的线程。
        //if we have less threads than maximum force creation of a new thread
        if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
        //if we reached here, we need to add it to the queue
        return super.offer(o);
    }

3.Dubbo线程池策略

Dubbo 提供3种线程池模型即:FixedThreadPool、CachedThreadPool(客户端默认的)、LimitedThreadPool(服务端默认的),从源码可以看出,其默认的队列长度都是0,当队列长度为0 ,其使用是无缓冲的队列SynchronousQueue,当运行线程超过maximumPoolSize则拒绝请求。

4.总结

线程池的任务队列本来起缓冲作用,但是如果设置的不合理会导致线程池无法扩容至max,这样无法发挥多线程的能力,导致一些服务响应变慢。

队列长度要看具体使用场景,取决服务端处理能力以及客户端能容忍的超时时间等

建议采用tomcat的处理方式,core与max一致,先扩容到max再放队列,不过队列长度要根据使用场景设置一个上限值,如果响应时间要求较高的系统可以设置为0。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值