珍贵的线程资源,JDK VS Tomcat

开发 前端
线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

哈喽,大家好,我是了不起。

高考结束了,依然祝高考学子们取得好成绩,然后报考计算机专业,然后快点毕业,来接收哥哥姐姐们留下的代码文明。。。。

接到某城举报,某迪泄露高考题目。

图片

最近生产环境各种问题,几百台设备停摆,真的是亚历山大。接手了别人的代码,只能硬着头皮改了,一不留神就恰班到10点多了。

今天还是说一下线程池的两个思考。

池子

我们常用的线程池,

1. JDK的ThreadPoolExecutor.

2. CompletableFutures 默认使用了ForkJoinPool.commonPool()。

CompletableFuture.supplyAsync(()->{ return "hello word";});

3. 还有Tomcat中的线程池

org.apache.tomcat.util.threads.TaskQueue  

org.apache.tomcat.util.threads.ThreadPoolExecutor

线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

JDK 线程池

public ThreadPoolExecutor(
    int corePoolSize, //核心线程数
    int maximumPoolSize,//最大线程数
    long keepAliveTime, //大于核心线程数量的线程存活时间,如果没有新任务就会关闭
    TimeUnit unit, // 时间单位
    BlockingQueue<Runnable> workQueue, //线程等待队列
    ThreadFactory threadFactory,//创建线程的工厂
    RejectedExecutionHandler handler//拒绝策略
) {

JDK线程池执行任务:

  1. 提交任务给线程池后,线程池会检查线程池中正在运行的线程数量,如果线程数量小于核心线程,则创建一个新的线程来处理任务。
  2. 如果线程池中线程数量达到和corePoolSize的大小,则将线程放入等待队列BlockingQueue中。
  3. 如果提交任务时连等待队列都已经满了的话,线程池会继续创建新的线程来处理任务,直到线程池数量达到maximumPoolSize。
  4. 如果线程数量达到了最大容量,则会执行拒绝策略。

这里需要注意直接使用LinkedBlockingQueue阻塞队列作为线程池会存在一个问题,当workcount > corePool时优先进入队列排队, 当请求并发过多时会导致请求缓慢,队列太长可能会出现内存溢出(先排队再涨线程池)

Tomcat线程池

下面时Tomcat线程池的构造方法

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
    this.ctl = new AtomicInteger(ctlOf(-536870912, 0));
    this.mainLock = new ReentrantLock();
    this.workers = new HashSet();
    this.termination = this.mainLock.newCondition();
    this.submittedCount = new AtomicInteger(0);
    this.lastContextStoppedTime = new AtomicLong(0L);
    this.lastTimeThreadKilledItself = new AtomicLong(0L);
    this.threadRenewalDelay = 1000L;
    if (corePoolSize >= 0 && maximumPoolSize > 0 && maximumPoolSize >= corePoolSize && keepAliveTime >= 0L) {
        if (workQueue != null && threadFactory != null && handler != null) {
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
            this.prestartAllCoreThreads();
        } else {
            throw new NullPointerException();
        }
    } else {
        throw new IllegalArgumentException();
    }
}

Tomcat主要针对web接口请求,不能因为LinkedBlockingQueue的排队导致接口出现大量延迟和缓慢, 从而使用了tomcat的TaskQueue,TaskQueue继承了JDK的LinkedBlockingQueue 并扩展了JDK线程池的功能。

主要有一下几点优化:

  • Tomcat的ThreadPoolExecutor使用的TaskQueue,是无界的LinkedBlockingQueue,但是通过taskQueue的offer方法覆盖了LinkedBlockingQueue的offer方法,修改了线程池增长规则,使得线程池能在任务较多的情况下增长线程池数量。(先涨线程池再排队。)
  • Tomcat的ThreadPoolExecutor改写了execute方法,当任务被reject时,捕获异常,会强制入队
public void execute(Runnable command, long timeout, TimeUnit unit) {
    this.submittedCount.incrementAndGet();

    try {
        this.executeInternal(command);
    } catch (RejectedExecutionException var9) {
        if (!(this.getQueue() instanceof TaskQueue)) {
            this.submittedCount.decrementAndGet();
            throw var9;
        }

        TaskQueue queue = (TaskQueue)this.getQueue();

        try {
            if (!queue.force(command, timeout, unit)) {
                this.submittedCount.decrementAndGet();
                throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));
            }
        } catch (InterruptedException var8) {
            this.submittedCount.decrementAndGet();
            throw new RejectedExecutionException(var8);
        }
    }

}

那个线程池适合

我们看看AI如何回复

图片

了不起认为大多数情况下使用JDK的线程池就够用了,如果觉得线程数据处理不过来,需要多一点线程直接增加核心线程数量设置就可以了。针对资源比较紧张,对线程使用代价比较高时可以考虑。

tomcat对线程池做过优化,也必然是有一定的考量,对于线程资源的使用频率比较高的情况下可以使用。

了不起之前也有对应的不同的例子来说明不同点。这里再放出来让大家体会一下!

JDK线程池,线程资源较珍贵

  • 有一家工厂建立,开始的时候只有10个工人,然后工厂的活越来越多,招聘新的工人肯定不是最好的策略,所以多出来的活暂时只能等着,进行排队。(这个例子中工厂的活多了,立马去招人肯定是不可能,只能先排单)
  • 后面工厂的业务越来越多,任务挤压过多,原来的工人干活已经不能满足业务需求了。为了最大化效益,招聘新的工人势在必行,于是就招聘了新的工人,所有的工人一起来干活,加快效率。
  • 当工厂的工人数量达到饱和之后,仍然不停的新增业务,此时工厂已经饱和,没有办法再继续接单。那么只能采取别的方案(拒绝策略),找别的工厂干,或者新建工厂。
  • 当后面业务量比较小的时候,新招的工人就会慢慢的裁剪(线程一段时间不使用就会关掉!)。

Tomcat线程池,线程资源使用比较频繁

  • 某学校有一个食堂,大厅里面日常至少摆100把椅子(min-spare)供学生们吃饭。
  • 然而当同时吃饭的同学大于100人的时候,食堂会增加一些椅子(创建线程),并且这些椅子也不会立马收回去,一段时间没有人使用才会收回。
  • 但是食堂里面最多可以摆500把椅子(maxThreads)。然后超过500人吃饭同时吃饭的话,其他人就只能在大厅里面排队等别人吃完。食堂大厅里面可以容纳1000人进行排队等候(maxConnections)。
  • 当食堂大厅1000人都排满了,那么就只能到食堂外面排队了,外面排队最多一直能排200人(acceptCount)。这个时候如果再有人过来要吃饭,而且还排不上队,就会等到不耐烦(time out),也会有人来告诉后来的同学,别来了人都满了,上其他地方吃饭去吧。(reject)
责任编辑:武晓燕 来源: Java技术指北
相关推荐

2021-08-20 08:22:12

Tomcat原生线程池

2010-11-10 08:57:51

Opera互联网浏览器

2009-07-09 14:02:58

Tomcat JDK

2009-11-24 09:26:49

LinuxJDKTomcat

2013-08-23 13:50:44

创业创业文化

2010-04-01 09:12:46

CentOS系统

2009-07-16 16:47:22

jdk的配置Tomcat的配置eclipse与MyE

2009-07-09 13:15:20

JDK1.6.0 To

2010-01-21 11:25:44

linux多线程线程资源

2020-05-20 11:17:29

缓存服务器地址

2019-09-19 14:55:01

物理CPU线程逻辑CPU

2009-07-07 15:45:17

JSP环境搭建

2009-07-07 15:41:56

myeclipse配置Tomcat安装tomcat插件

2009-06-03 16:33:53

安装jdk tomcaeclipse配置

2023-02-07 20:17:21

JDK19虚拟线程

2023-11-01 07:24:16

JDK19线程

2018-04-27 10:35:08

Tomcat连接数线程池

2009-07-10 16:31:01

Ubuntu下安装JD安装Tomcat

2009-07-03 11:36:47

JSP教程
点赞
收藏

51CTO技术栈公众号