springboot tomcat默认线程数_记一次JAVA线程池的错误用法

最近项目一个项目要结项了,但客户要求 TPS 能达到上千,而用我写的代码再怎么弄成只能达到 30 + 的 TPS,然后我又将代码中能缓存的都缓存了,能拆分的也都拆分了,拆分时用的线程池来实现的;其实现的代码主要为以前写的一篇博客中的实现方式来实现的。如下:

多线程之 futureTask(future,callable) 实例, jdbc 数据多线程查询https://blog.csdn.net/puhaiyang/article/details/78041046

在其中用到了线程池,为了方便,线程池是采用如下代码 new 出来的

final ExecutorService executorService = Executors.newFixedThreadPool(10);  

通过自己仔细想想后,感觉这代码总有哪里写得不对,为此特意写了下 DEMO 代码来,并用 jmeter 自己跑一下自己测下:

  @RequestMapping(value = "doTest")
    public Object doTest(@RequestParam(defaultValue = "false") Boolean shutdown,
                         @RequestParam(defaultValue = "10") Integer threadCount,
                         @RequestParam(defaultValue = "100") Integer sleepTime,
                         @RequestParam(defaultValue = "10") Integer queryCount) {
        long beginTime = System.currentTimeMillis();
        final ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        for (int i = 0; i             int finalI = i;
            Callable callable = new Callable() {
                @Override
                public Integer call() throws Exception {
                    Thread.sleep(sleepTime);
                    logger.debug("index:{} threadInfo:{}", finalI, Thread.currentThread().toString());return finalI;
                }
            };
            FutureTask futureTask = new FutureTask(callable);
            executorService.submit(futureTask);
        }if (shutdown) {
            executorService.shutdown();
        }
        Long endTime = System.currentTimeMillis();
        endTime = endTime - beginTime;
        logger.info("info:{}", endTime);return atomicInteger.addAndGet(endTime.intValue()) + "   this:" + endTime;
    } 

代码如上所示,然后我用 jmeter 对此进行了测试,测试 1000 个请求去访问,每个任务线程休眠时间设的为 1 秒,TPS 为 20 多。

一想这确实挺低的,然后分析其原因,想着是不是 springBoot 的线程数给的太少了,于是乎又把 tomcat 的最大线程数进行了修改,由默认的 200 修改为了 500,但发现没啥大的变化,想了想后,可能问题不是 tomcat 的配置导致的。

server:
  tomcat:
    max-threads: 500 

然后又通过 Java VisualVM 工具看了看线程信息,没发现啥问题。

然后出去静了静,听了一两首音乐后想着起来 Executors 还有一个 newCachedThreadPool() 的用法,它与 newFixedThreadPool() 的区别通过源码可以大概知道个一二:

newFixedThreadPool:

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }

newCachedThreadPool:

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

newFixedThreadPool 是创建一个大小固定的线程池,线程数固定,也不会被回收

newCachedThreadPool 是创建一个大小为 MAX_VALUE 的线程数,并具有缓存功能,如果 60 秒内线程一直 处于空闲,则会进行回收

另外,线程池的 shutdown 方法 doc 文档的解释如下:

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down. This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.

指的是等待线程池执行 shutdown 方法后就不再接收新的执行目标了,等当前线程池中的现场执行完毕后,此线程池就会关闭掉了。

通过查看 JAVA 源码中的注释信息后才得知原来我之前写的代码有了一个大大的 BUG,不应该执行完一次后就立即把线程池给 shutdown 掉,这样的话,线程池的意义就没多大的意思了,跟 new Thread 的就差不多了。尴尬了!

然后将测试代码修改为如下的代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
 
@RestController
@RequestMapping(value = "test")
public class TestController {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
 
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    final ExecutorService executorService = Executors.newCachedThreadPool();
 
    @RequestMapping(value = "doTest")
    public Object doTest(@RequestParam(defaultValue = "false") Boolean shutdown,
                         @RequestParam(defaultValue = "10") Integer threadCount,
                         @RequestParam(defaultValue = "100") Integer sleepTime,
                         @RequestParam(defaultValue = "10") Integer queryCount) {
        long beginTime = System.currentTimeMillis();
//        final ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        for (int i = 0; i             int finalI = i;
            Callable callable = new Callable() {
                @Override
                public Integer call() throws Exception {
                    Thread.sleep(sleepTime);
                    logger.debug("index:{} threadInfo:{}", finalI, Thread.currentThread().toString());return finalI;
                }
            };
            FutureTask futureTask = new FutureTask(callable);
            executorService.submit(futureTask);
        }if (shutdown) {
            executorService.shutdown();
        }
        Long endTime = System.currentTimeMillis();
        endTime = endTime - beginTime;
        logger.info("info:{}", endTime);return atomicInteger.addAndGet(endTime.intValue()) + "   this:" + endTime;
    }
}

调用时,shutdown 传入 false,并且线程池的 new 方法放到上面的公共方法区域中,而不应该是来一个请求就 new 一个线程池出来。然后将同样的请求用 jmeter 测试后,发现能达到 300 多了,比之前的 20 多提升了许多倍!

总结:

通过上面的测试,发现了一个我之前用错的 JAVA 线程池的用法,通过 jmeter 工具测试后才知道其性能是如何的大。

同时在通过修改 springboot 的配置信息后,发现 springBoot 能创建的线程池最大线程数也与其 tomcat 的最大线程数有关,具体身体关系还得靠后面的慢慢探索了。(贼尴尬,这么基础的代码问题居然给犯下了这么大一个错误. 还好及时地修改了。哈哈)


作者:水中加点糖

来源链接:

https://blog.csdn.net/puhaiyang/article/details/80530495

695150a34d934f7e08811417da81691d.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379132.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

引以为鉴-ARM开发板连线注意事项

前些日子把实验室的三台机子放到一个工位上&#xff0c;非常拥挤&#xff0c;做实验也很不方便。因此&#xff0c;想把ARM开发板的环境重新搭建到自己的电脑上。说完就做&#xff0c;上午就开始忙活起来。把开发板上的USB线、串口线、JTAT接口、还有电源线一一插好。接着就开始…

Python---实验八

1&#xff0c;现在有一份‘邀请函.txt’的空白文件&#xff0c;请在同级目录下编写一段代码&#xff0c;写入内容‘诚挚邀请您来参加本次宴会’。 with open(fG:\study\Python\邀请函.txt,modew,encodingutf-8) as y:y.write(诚挚邀请您来参加本次宴会)效果图如下&#xff1a;…

哈希表 - (代码、分析 )

目录&#xff1a;代码&#xff1a;分析&#xff1a;代码&#xff1a; BSTree.h BSTree.c 二叉排序树(Binary Sort Tree) 又称为二叉查找树(Binary Search Tree) Hash.h #ifndef _HASH_H_ #define _HASH_H_typedef void Hash;//定义哈希表类型 typedef void HashKey;//定义哈…

scala spark 数据对比_IT大牛耗时三个月总结出大数据领域学习路线,网友评论:炸锅了...

大数据不是某个专业或一门编程语言&#xff0c;实际上它是一系列技术的组合运用。有人通过下方的等式给出了大数据的定义。大数据 编程技巧 数据结构和算法 分析能力 数据库技能 数学 机器学习 NLP OS 密码学 并行编程虽然这个等式看起来很长&#xff0c;需要学习的东…

Python---实验九作业

1&#xff0c;使用tkinter实现计算器程序。实现效果如下&#xff1a; from tkinter import * from tkinter.ttk import *def frame(master):"""将共同的属性作为默认值, 以简化Frame创建过程"""w Frame(master)w.pack(sideTOP, expandYES, fill…

用pv操作描述如下前驱图_LinkedList实现分析(二)——常用操作

上一篇文章LinkedList实现分析(一)——LinkedList初探与对象创建介绍了LinkedList中的一些重要属性和构造方法&#xff0c;下面我们将详细介绍一下LinkedList提高的常用方法的实现原理元素添加###add(E e)方法往LinkedList添加元素&#xff0c;LinkedList提供了多重方式&#x…

C++多重继承与虚基类及与.NET的比较

多重继承前面我们介绍的派生类只有一个基类&#xff0c;称为单基派生或单一继承。在实际运用中&#xff0c;我们经常需要派生类同时具有多个基类&#xff0c;这种方法称为多基派生或多重继承。2.1 多重继承的声明&#xff1a;在 C 中&#xff0c;声明具有两个以上基类的派生类与…

平院实训门禁系统导入

这是我的配置&#xff08;如果是Win10最好每一步都管理员身份运行&#xff09; win7 SQLServer2008 VS2012 切记&#xff1a;注意&#xff1a;当你SQLserver创建数据库和VS连接数据库的时候得用同一种方式&#xff0c;要么都用window&#xff08;主机名&#xff09;&#xff0…

北京中信银行总行地址_中信银行拉萨分行举行“存款保险标识”启用和存款保险条例宣传活动...

11月NOV中信银行拉萨分行举行“存款保险标识”启用和《存款保险条例》宣传活动揭牌启用仪式111月Jul根据人民银行和总行关于“存款保险标识”启用工作相关要求&#xff0c;分行行领导高度重视“存款保险标识”启用和《存款保险条例》宣传活动工作&#xff0c;按照统一工作部署、…

转整型_156.Ruby烘焙大理石豆沙吐司解锁大理石花纹整型

好看又好吃的大理石豆沙面包。红豆馅均匀分布在松软细腻的面包体里&#xff0c;手撕着吃&#xff0c;一层层的甜美与温柔&#xff5e;关于吐司面包&#xff0c;我公众号里写过白吐司(基础款牛奶吐司&#xff0c;超绵鲜奶油吐司)和全麦吐司(基础款50%全麦吐司&#xff0c;经典燕…

VS2010 快捷键 (空格显示 绿点, Tab 显示箭头)

VS2010 有用的快捷键 &#xff1a; Ctrl r, ctrl w, 切换空格示。 转载于:https://www.cnblogs.com/fengye87626/archive/2012/11/21/2780716.html

分析一下mp4格式的trak -> mdia -> minf -> stbl -> stts、stsc 这两个box信息

分析一下mp4格式的trak -> mdia -> minf -> stbl -> stts、stsc 这两个box信息 &#xff08;因为这两个box在音频trak和视频trak 下都有的&#xff0c;而且都有一个数组的值是比较绕的&#xff09; 目录&#xff1a;stts&#xff1a;记录时间戳的&#xff0c;每个s…

Python---爬虫案例

例1、爬取公众号文章中的图片。 1&#xff0c;首先打开要获取公众号文章的地址 2&#xff0c;按下F12&#xff0c;再按Ctrl Shift C&#xff0c;然后鼠标移动到图片位置&#xff0c;然后观察控制台中显示图片对应的代码位置 3&#xff0c;分析该位置的代码段 代码段如下&…

Python---实验九

1、使用标准库urllib爬取“http://news.pdsu.edu.cn/info/1005/31269.htm”平顶山学院新闻网上的图片&#xff0c;要求:保存到F盘pic目录中&#xff0c;文件名称命名规则为“本人姓名” “_图片编号”&#xff0c;如姓名为张三的第一张图片命名为“张三_1.jpg”。 from re imp…

32接上拉5v_51单片机P0口上拉电阻的选择

作为I/O口输出的时候时&#xff0c;输出低电平为0 输出高电平为高组态(并非5V&#xff0c;相当于悬空状态&#xff0c;也就是说P0 口不能真正的输出高电平)。给所接的负载提供电流&#xff0c;因此必须接(一电阻连接到VCC)&#xff0c;由电源通过这个上拉电阻给负载提供电流。P…

[转载]FPGA/CPLD重要设计思想及工程应用(时序及同步设计)

来源&#xff1a;http://www.eetop.cn/blog/html/11/317611-13412.html 数字电路中,时钟是整个电路最重要、最特殊的信号。 第一, 系统内大部分器件的动作都是在时钟的跳变沿上进行, 这就要求时钟信号时延差要非常小, 否则就可能造成时序逻辑状态出错. 第二, 时钟信号通常是系统…

实验五 图形设计

每复制一个方法都要绑定Paint事件 一、创建Windows窗体应用程序&#xff0c;要求如下&#xff1a;&#xff08;源代码运行界面&#xff0c;缺少任一项为0分&#xff0c;源代码只需粘贴绘制图形代码所在的方法&#xff0c;不用粘贴太多&#xff09; 例如: &#xff08;1&…

ADO.NET与SQL Server数据库的交互

7.3.1 使用SqlConnection对象连接数据库 例如&#xff1a;建立与SQL Server数据库的连接。 string connstring"Data Sourceservername;uidusername;pwdpassword;Initial Catalogdbname";SqlConnection connnew SqlConnection(connstring);conn.Open(); 例如&#xf…

linux ftp日志_linux学习笔记(一)——Linux分区和目录结构

linux学习笔记&#xff08;一&#xff09;——Linux分区和目录结构安装Linux时&#xff0c;手动挂载分区的情况下&#xff0c;/ 和 swap 是必须要挂载的&#xff0c;其他/home、/boot 等可以根据需要自行挂载。一般来说&#xff0c;简单的话&#xff0c;建议挂载三个分区&#…

vc++ 6.0 堆栈_在C ++中使用链接列表实现堆栈

vc 6.0 堆栈To implement a stack using a linked list, basically we need to implement the push() and pop() operations of a stack using linked list. 要使用链接列表实现堆栈 &#xff0c;基本上&#xff0c;我们需要使用链接列表实现堆栈的push()和pop()操作。 Exampl…