java线程池中线程数量到底是几

目录
  • 线程池配置
  • 线程池里的业务线程数量小于最小数量(5)
    • 第一个请求
    • 第二个请求
    • 第三个请求
    • 第五个请求
  • 小于阻塞队列容量(10)
    • 第六个请求
    • 第七个请求
    • 第15个请求
  • 小于最大数量(20)
    • 第16个请求
    • 第35个请求
  • 拒绝策略
    • 第36个请求
  • 复用线程

线程池配置

线程池配置,假设是:

1.最小数量是5

2.阻塞队列容量是10

3.最大数量是20

线程池里的业务线程数量小于最小数量(5)

第一个请求

第一个请求进来的时候,这个时候,线程池没有线程,就创建新的工作线程(即Worker线程)。

然后,这个工作线程去处理当前请求的业务线程。

第二个请求

第二个请求进来的时候,这个时候,线程池已经有了一个工作线程。

但是,要注意,这个时候是不会复用线程池里已有的工作线程的。而是创建新的工作线程。

因为,线程池里根本没有复用线程的概念。

说白了,无论线程池里已有的这个工作线程是否在处理业务线程,即不管它空闲与否,其实都会创建新的工作线程。

第三个请求

同上,仍然创建新的工作线程。

。。。

第五个请求

同上。仍然创建新的工作线程。

注意,现在,线程池有几个工作线程?5个。

即,每个请求进来,都创建一个新的工作线程。

小于阻塞队列容量(10)

第六个请求

第六个请求进来的时候,提交到阻塞队列。

然后,再慢慢消费。

具体来说,是由线程池里的工作线程来慢慢消费。

具体消费的源码,参考:复用线程小节。

第七个请求

同上,也是先添加到阻塞队列。

。。。

第15个请求

同上,也是先添加到阻塞队列。

小于最大数量(20)

第16个请求

先来看,正常情况下,阻塞队列还没塞满(生产环境的容量一般是1000),就会被快速处理掉。

然后,当新的请求进来的时候,继续丢到阻塞队列里面去。

这个是和上面讲的一样。

但是,我们为了方便理解,现在假设之前的15个请求是同时到达,即

  • 前面5个请求

创建5个新的请求。

  • 后面10个请求

全部丢到阻塞队列。

这个时候,阻塞队列已经满了。接着,第16个请求进来了,怎么办?

继续创建新的工作线程。

。。。

第35个请求

同上,继续创建新的工作线程。

注意,这个时候,线程池里的工作线程的数量是多少?20。

因为

  • 前面5个请求,创建了5个新的工作线程。
  • 最后面的15个请求(第16到第35),创建了15个新的工作线程。

所以,总共,创建了20个新的工作线程。线程池,总共有20个工作线程。

拒绝策略

第36个请求

假设前面的请求都没有处理完,这个时候,来了第36个请求,怎么办?

只能采取拒绝策略。

具体采用哪个拒绝策略?比如说,一般情况下,都是采用丢弃。

复用线程

前面说了,线程池里没有复用线程的概念。

那到底是怎么回事呢?既然不能复用线程,那搞个线程池有个几把用?

具体是这样子,虽然,线程池里的工作线程不能被复用,仅仅是指类似数据库连接池里的连接的那种复用,即

  • 用的时候,从连接池取
  • 用完了,归还到连接池

线程池里的对象复用,是基于循环,而不是用完之后再还回去。

什么意思呢?就是工作线程,不断的从阻塞队列里取业务线程,然后执行业务线程。

伪代码

工作线程{
  run(){
    while(){
      1.从阻塞队列,取业务线程
      2.执行业务线程;
    }
  }
}

所以,线程池和连接池的区别在于,线程池的对象是线程,可以不断的循环读业务线程。而连接池的对象,是用完了归还到连接池里去。

jdk源码-java.util.concurrent.ThreadPoolExecutor#runWorker

/**
     * 核心步骤
     * 1.从阻塞队列,读业务线程
     * 2.执行业务线程
     *
     * ---
     * Main worker run loop.  Repeatedly gets tasks from queue and
     * executes them, while coping with a number of issues:
     *
     * 1. We may start out with an initial task, in which case we
     * don't need to get the first one. Otherwise, as long as pool is
     * running, we get tasks from getTask. If it returns null then the
     * worker exits due to changed pool state or configuration
     * parameters.  Other exits result from exception throws in
     * external code, in which case completedAbruptly holds, which
     * usually leads processWorkerExit to replace this thread.
     *
     * 2. Before running any task, the lock is acquired to prevent
     * other pool interrupts while the task is executing, and then we
     * ensure that unless pool is stopping, this thread does not have
     * its interrupt set.
     *
     * 3. Each task run is preceded by a call to beforeExecute, which
     * might throw an exception, in which case we cause thread to die
     * (breaking loop with completedAbruptly true) without processing
     * the task.
     *
     * 4. Assuming beforeExecute completes normally, we run the task,
     * gathering any of its thrown exceptions to send to afterExecute.
     * We separately handle RuntimeException, Error (both of which the
     * specs guarantee that we trap) and arbitrary Throwables.
     * Because we cannot rethrow Throwables within Runnable.run, we
     * wrap them within Errors on the way out (to the thread's
     * UncaughtExceptionHandler).  Any thrown exception also
     * conservatively causes thread to die.
     *
     * 5. After task.run completes, we call afterExecute, which may
     * also throw an exception, which will also cause thread to
     * die. According to JLS Sec 14.20, this exception is the one that
     * will be in effect even if task.run throws.
     *
     * The net effect of the exception mechanics is that afterExecute
     * and the thread's UncaughtExceptionHandler have as accurate
     * information as we can provide about any problems encountered by
     * user code.
     *
     * @param w the worker
     */
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //从阻塞队列里获取业务线程
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //执行业务线程
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

到此这篇关于java线程池中线程数量到底是几的文章就介绍到这了,更多相关java 线程数量内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Java线程池的7大核心参数

    前言 java中经常需要用到多线程来处理一些业务,我不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源.线程上下文切换问题. 同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理. java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括: Executor.Executors.ExecutorService.ThreadPoolE

  • Java线程池中的各个参数如何合理设置

    一.前言 在开发过程中,好多场景要用到线程池.每次都是自己根据业务场景来设置线程池中的各个参数. 这两天又有需求碰到了,索性总结一下方便以后再遇到可以直接看着用. 虽说根据业务场景来设置各个参数的值,但有些万变不离其宗,掌握它的原理对如何用好线程池起了至关重要的作用. 那我们接下来就来进行线程池的分析. 二.ThreadPoolExecutor的重要参数 我们先来看下ThreadPoolExecutor的带的那些重要参数的构造器. public ThreadPoolExecutor(int co

  • java线程池合理设置最大线程数和核心线程数方式

    目录 线程池合理设置最大线程数和核心线程数 一开始是这么配置的 后来网上查询线程池核心数配置 最后我是这么配置的 线程池核心线程数与最大线程数的区别 线程池策略 饱和策略 线程池合理设置最大线程数和核心线程数 工作中有这样一个场景,需要处理千万级别的数据的一个算法,大部分是增删查的操作.这个时候就需要使用多线程去处理. 一开始是这么配置的 @Configuration @EnableAsync(proxyTargetClass = true)//利用@EnableAsync注解开启异步任务支持

  • JAVA 自定义线程池的最大线程数设置方法

    一:CPU密集型: 定义:CPU密集型也是指计算密集型,大部分时间用来做计算逻辑判断等CPU动作的程序称为CPU密集型任务.该类型的任务需要进行大量的计算,主要消耗CPU资源.  这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数. 特点:    01:CPU 使用率较高(也就是经常计算一些复杂的运算,逻辑处理等情况)非常多的情况下使用    02:针对单台机

  • Java多线程之线程池七个参数详解

    ThreadPoolExecutor是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它提供了任务提交.线程管理.监控等方法. 下面是ThreadPoolExecutor类的构造方法源码,其他创建线程池的方法最终都会导向这个构造方法,共有7个参数:corePoolSize.maximumPoolSize.keepAliveTime.unit.workQueue.threadFactory.handler. public ThreadPoolExecutor(int corePoolS

  • java线程池中线程数量到底是几

    目录 线程池配置 线程池里的业务线程数量小于最小数量(5) 第一个请求 第二个请求 第三个请求 第五个请求 小于阻塞队列容量(10) 第六个请求 第七个请求 第15个请求 小于最大数量(20) 第16个请求 第35个请求 拒绝策略 第36个请求 复用线程 线程池配置 线程池配置,假设是: 1.最小数量是5 2.阻塞队列容量是10 3.最大数量是20 线程池里的业务线程数量小于最小数量(5) 第一个请求 第一个请求进来的时候,这个时候,线程池没有线程,就创建新的工作线程(即Worker线程). 然

  • 详解Java并发包中线程池ThreadPoolExecutor

    一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要开销的.而线程池中的线程是可复用的,不需要每次执行异步任务的时候重新创建和销毁线程:②线程池提供一种资源限制和管理的手段,比如可以限制线程的个数,动态的新增线程等等. 在下面的分析中,我们可以看到,线程池使用一个Integer的原子类型变量来记录线程池状态和线程池中的线程数量,通过线程池状态来控制任

  • java线程池中Worker线程执行流程原理解析

    目录 引言 Worker类分析 runWorker(Worker)方法 getTask()方法 beforeExecute(Thread, Runnable)方法 afterExecute(Runnable, Throwable)方法 processWorkerExit(Worker, boolean)方法 tryTerminate()方法 terminated()方法 引言 在<[高并发]别闹了,这样理解线程池执行任务的核心流程才正确!!>一文中我们深度分析了线程池执行任务的核心流程,在Th

  • Java concurrency线程池之线程池原理(一)_动力节点Java学院整理

    ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存放一定数量线程的一个线程集合.线程池允许若个线程同时允许,允许同时运行的线程数量就是线程池的容量:当添加的到线程池中的线程超过它的容量时,会有一部分线程阻塞等待.线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理." ThreadPoolExecutor数据结构 ThreadPoolExecutor的数据结构如下图所示: 各个数据在Thread

  • Java concurrency线程池之线程池原理(二)_动力节点Java学院整理

    线程池示例 在分析线程池之前,先看一个简单的线程池示例. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class ThreadPoolDemo1 { public static void main(String[] args) { // 创建一个可重用固定线程数的线程池 ExecutorService pool = Executors.newFixedThre

  • Java 自定义线程池和线程总数控制操作

    1 概述 池化是常见的思想,线程池是非常典型的池化的实现,<Java并发编程实战>也大篇幅去讲解了Java中的线程池.本文实现一个简单的线程池. 2 核心类 [1]接口定义 public interface IThreadPool<Job extends Runnable> { /** * 关闭线程池 */ public void shutAlldown(); /** * 执行任务 * * @param job 任务 */ public void execute(Job job);

  • 线程池中execute与submit的区别说明

    目录 线程池execute与submit区别 execute submit 例1 例2 线程池submit和execute方法原理 线程池的作用 线程池的原理 线程池execute与submit区别 在使用线程池的时候,看到execute()与submit()方法.都可以使用线程池执行一个任务,但是两者有什么区别呢? execute void execute(Runnable command); submit <T> Future<T> submit(Callable<T&g

  • Springboot 配置线程池创建线程及配置 @Async 异步操作线程池详解

    目录 前言 一.创建一个Springboot Web项目 二.新建ThreadPoolConfig 三.新建controller测试 四.演示结果 前言 众所周知,创建显示线程和直接使用未配置的线程池创建线程,都会被阿里的大佬给diss,所以我们要规范的创建线程. 至于 @Async 异步任务的用处是不想等待方法执行完就返回结果,提高软件前台响应速度,一个程序中会用到很多异步方法,所以需要使用线程池管理,防止影响性能. 一.创建一个Springboot Web项目 需要一个Springboot项

  • Java线程池中多余的线程是如何回收的

    最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了. 不过,我倒是对线程池是如何回收工作线程比较感兴趣,所以简单分析了一下,加深对线程池的理解吧. 那么,就以JDK1.8为例分析吧. 1.runWorker(Worker w) 工作线程启动后,就进入runWorker(Worker w)方法. 里面是一个while循环,循环判断任务是否为空,若不为空,执行任务:若取不到任务,或发生异

  • Java实现终止线程池中正在运行的定时任务

    最近项目中遇到了一个新的需求,就是实现一个可以动态添加定时任务的功能.说到这里,有人可能会说简单啊,使用quartz就好了,简单粗暴.然而quartz框架太重了,小项目根本不好操作啊.当然,也有人会说,jdk提供了timer的接口啊,完全够用啊.但是我们项目的需求完全是多线程的模型啊,而timer是单线程的,so,楼主最后还是选择了jdk的线程池. 线程池是什么 Java通过Executors提供四种线程池,分别为: newCachedThreadPool :创建一个可缓存线程池,如果线程池长度

随机推荐