Java线程池实现原理及其在美团业务中的实践

随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流。使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器。J.U.C提供的线程池:ThreadPoolExecutor类,帮助开发人员管理线程并方便地执行并行任务。了解并合理使用线程池,是一个开发人员必修的基本功。

本文开篇简述线程池概念和用途,接着结合线程池的源码,帮助读者领略线程池的设计思路,最后回归实践,通过案例讲述使用线程池遇到的问题,并给出了一种动态化线程池解决方案。

线程池(ThreadPool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。

线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

而本文描述线程池是JDK中提供的ThreadPoolExecutor类。

当然,使用线程池可以带来一系列好处:

线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:

为解决资源分配这个问题,线程池采用了“池化”(Pooling)思想。池化,顾名思义,是为了最大化收益并最小化风险,而将资源统一在一起管理的一种思想。

Poolingisthegroupingtogetherofresources(assets,equipment,personnel,effort,etc.)forthepurposesofmaximizingadvantageorminimizingrisktotheusers.Thetermisusedinfinance,computingandequipmentmanagement.——wikipedia

在计算机领域中的表现为:统一管理IT资源,包括服务器、存储、和网络资源等等。通过共享资源,使用户在低投入中获益。除去线程池,还有其他比较典型的几种使用策略包括:

在了解完“是什么”和“为什么”之后,下面我们来一起深入一下线程池的内部实现原理。

在前文中,我们了解到:线程池是一种通过“池化”思想,帮助我们管理线程而获取并发性的工具,在Java中的体现是ThreadPoolExecutor类。那么它的的详细设计与实现是什么样的呢?我们会在本章进行详细介绍。

Java中的线程池核心实现类是ThreadPoolExecutor,本章基于JDK1.8的源码来分析Java线程池的核心设计与实现。我们首先来看一下ThreadPoolExecutor的UML类图,了解下ThreadPoolExecutor的继承关系。

ThreadPoolExecutor是如何运行,如何同时维护线程和执行任务的呢?其运行机制如下图所示:

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:(1)直接申请线程执行该任务;(2)缓冲到队列中等待线程执行;(3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

接下来,我们会按照以下三个部分去详细讲解线程池运行机制:

线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量(workerCount)。在具体实现中,线程池将运行状态(runState)、线程数量(workerCount)两个关键参数的维护放在了一起,如下代码所示:

关于内部封装的获取生命周期状态、获取线程池线程数量的计算方法如以下代码所示:

privatestaticintrunStateOf(intc){returnc&~CAPACITY;}//计算当前运行状态privatestaticintworkerCountOf(intc){returnc&CAPACITY;}//计算当前线程数量privatestaticintctlOf(intrs,intwc){returnrs|wc;}//通过状态和线程数生成ctlThreadPoolExecutor的运行状态有5种,分别为:

其生命周期转换如下入所示:

2.3.1任务调度

任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。

首先,所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:

其执行流程如下图所示:

2.3.2任务缓冲

任务缓冲模块是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池中是以生产者消费者模式,通过一个阻塞队列来实现的。阻塞队列缓存任务,工作线程从阻塞队列中获取任务。

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

下图中展示了线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素:

使用不同的队列可以实现不一样的任务存取策略。在这里,我们可以再介绍下阻塞队列的成员:

2.3.3任务申请

由上文的任务分配部分可知,任务的执行有两种可能:一种是任务直接由新创建的线程执行。另一种是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。

线程需要从任务缓存模块中不断地取任务执行,帮助线程从阻塞队列中获取任务,实现线程管理模块和任务管理模块之间的通信。这部分策略由getTask方法实现,其执行流程如下图所示:

getTask这部分进行了多次判断,为的是控制线程的数量,使其符合线程池的状态。如果线程池现在不应该持有那么多线程,则会返回null值。工作线程Worker会不断接收新任务去执行,而当工作线程Worker接收不到任务的时候,就会开始被回收。

2.3.4任务拒绝

任务拒绝模块是线程池的保护部分,线程池有一个最大的容量,当线程池的任务缓存队列已满,并且线程池中的线程数目达到maximumPoolSize时,就需要拒绝掉该任务,采取任务拒绝策略,保护线程池。

拒绝策略是一个接口,其设计如下:

publicinterfaceRejectedExecutionHandler{voidrejectedExecution(Runnabler,ThreadPoolExecutorexecutor);}用户可以通过实现这个接口去定制拒绝策略,也可以选择JDK提供的四种已有拒绝策略,其特点如下:

2.4Worker线程管理

2.4.1Worker线程

线程池为了掌握线程的状态并维护线程的生命周期,设计了线程池内的工作线程Worker。我们来看一下它的部分代码:

privatefinalclassWorkerextendsAbstractQueuedSynchronizerimplementsRunnable{finalThreadthread;//Worker持有的线程RunnablefirstTask;//初始化的任务,可以为null}Worker这个工作线程,实现了Runnable接口,并持有一个线程thread,一个初始化的任务firstTask。thread是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务;firstTask用它来保存传入的第一个任务,这个任务可以有也可以为null。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。

Worker执行任务的模型如下图所示:

Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。

1.lock方法一旦获取了独占锁,表示当前线程正在执行任务中。2.如果正在执行任务,则不应该中断线程。3.如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。4.线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。

在线程回收过程中就使用到了这种特性,回收过程如下图所示:

2.4.2Worker线程增加

增加线程是通过线程池中的addWorker方法,该方法的功能就是增加一个线程,该方法不考虑线程池是在哪个阶段增加的该线程,这个分配线程的策略是在上个步骤完成的,该步骤仅仅完成增加线程,并使它运行,最后返回是否成功这个结果。addWorker方法有两个参数:firstTask、core。firstTask参数用于指定新增的线程执行的第一个任务,该参数可以为空;core参数为true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,false表示新增线程前需要判断当前活动线程数是否少于maximumPoolSize,其执行流程如下图所示:

2.4.3Worker线程回收

线程池中线程的销毁依赖JVM自动的回收,线程池做的工作是根据当前线程池的状态维护一定数量的线程引用,防止这部分线程被JVM回收,当线程池决定哪些线程需要回收时,只需要将其引用消除即可。Worker被创建出来后,就会不断地进行轮询,然后获取任务去执行,核心线程可以无限等待获取任务,非核心线程要限时获取任务。当Worker无法获取到任务,也就是获取的任务为空时,循环会结束,Worker会主动消除自身在线程池内的引用。

try{while(task!=null||(task=getTask())!=null){//执行任务}}finally{processWorkerExit(w,completedAbruptly);//获取不到任务时,主动回收自己}线程回收的工作是在processWorkerExit方法完成的。

事实上,在这个方法中,将线程引用移出线程池就已经结束了线程销毁的部分。但由于引起线程销毁的可能性有很多,线程池还要判断是什么引发了这次销毁,是否要改变线程池的现阶段状态,是否要根据新状态,重新分配线程。

2.4.4Worker线程执行任务

在Worker类中的run方法调用了runWorker方法来执行任务,runWorker方法的执行过程如下:

1.while循环不断地通过getTask()方法获取任务。2.getTask()方法从阻塞队列中取任务。3.如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态。4.执行任务。5.如果getTask结果为null则跳出循环,执行processWorkerExit()方法,销毁线程。

执行流程如下图所示:

在当今的互联网业界,为了最大程度利用CPU的多核性能,并行运算的能力是不可或缺的。通过线程池管理线程获取并发性是一个非常基础的操作,让我们来看两个典型的使用线程池获取并发性的场景。

场景1:快速响应用户请求

场景2:快速处理批量任务

描述:离线的大量计算任务,需要快速执行。比如说,统计某个报表,需要计算出全国各个门店中有哪些商品有某种属性,用于后续营销策略的分析,那么我们需要查询全国所有门店中的所有商品,并且记录具有某属性的商品,然后快速生成报表。

关于线程池配置不合理引发的故障,公司内部有较多记录,下面举一些例子:

Case1:2018年XX页面展示接口大量调用降级:

事故描述:XX页面展示接口产生大量调用降级,数量级在几十到上百。

事故原因:该服务展示接口内部逻辑使用线程池做并行计算,由于没有预估好调用的流量,导致最大核心数设置偏小,大量抛出RejectedExecutionException,触发接口降级条件,示意图如下:

Case2:2018年XX业务服务不可用S2级故障

业务中要使用线程池,而使用不当又会导致故障,那么我们怎样才能更好地使用线程池呢?针对这个问题,我们下面延展几个方向:

1.能否不用线程池

回到最初的问题,业务使用线程池是为了获取并发性,对于获取并发性,是否可以有什么其他的方案呢替代?我们尝试进行了一些其他方案的调研:

综合考虑,这些新的方案都能在某种情况下提升并行任务的性能,然而本次重点解决的问题是如何更简易、更安全地获得的并发性。另外,Actor模型的应用实际上甚少,只在Scala中使用广泛,协程框架在Java中维护的也不成熟。这三者现阶段都不是足够的易用,也并不能解决业务上现阶段的问题。

2.追求参数设置合理性?

有没有一种计算公式,能够让开发同学很简易地计算出某种场景中的线程池应该是什么参数呢?

带着这样的疑问,我们调研了业界的一些线程池参数配置方案:

3.线程池参数动态化?

基于以上三个方向对比,我们可以看出参数动态化方向简单有效。

3.3.1整体设计

动态化线程池的核心设计包括以下三个方面:

3.3.2功能架构

动态化线程池提供如下功能:

参数动态化

JDK原生线程池ThreadPoolExecutor提供了如下几个public的setter方法,如下图所示:

JDK允许线程池使用方通过ThreadPoolExecutor的实例来动态设置线程池的核心策略,以setCorePoolSize为方法例,在运行期线程池使用方调用此方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。对于当前值小于当前工作线程数的情况,说明有多余的worker线程,此时会向当前idle的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收;对于当前值大于原始值且当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务,setCorePoolSize具体流程如下:

线程池内部会处理好当前状态做到平滑修改,其他几个方法限于篇幅,这里不一一介绍。重点是基于这几个public方法,我们只需要维护ThreadPoolExecutor的实例,并且在需要修改的时候拿到实例修改其参数即可。基于以上的思路,我们实现了线程池参数的动态化、线程池参数在管理平台可配置可修改,其效果图如下图所示:

用户可以在管理平台上通过线程池的名字找到指定的线程池,然后对其参数进行修改,保存后会实时生效。目前支持的动态参数包括核心数、最大值、队列长度等。除此之外,在界面中,我们还能看到用户可以配置是否开启告警、队列等待任务告警阈值、活跃度告警等等。关于监控和告警,我们下面一节会对齐进行介绍。

线程池监控

在传统的线程池应用场景中,线程池中的任务执行情况对于用户来说是透明的。比如在一个具体的业务场景中,业务开发申请了一个线程池同时用于执行两种任务,一个是发消息任务、一个是发短信任务,这两类任务实际执行的频率和时长对于用户来说没有一个直观的感受,很可能这两类任务不适合共享一个线程池,但是由于用户无法感知,因此也无从优化。动态化线程池内部实现了任务级别的埋点,且允许为不同的业务任务指定具有业务含义的名称,线程池内部基于这个名称做Transaction打点,基于这个功能,用户可以看到线程池内部任务级别的执行情况,且区分业务,任务监控示意图如下图所示:

用户基于JDK原生线程池ThreadPoolExecutor提供的几个public的getter方法,可以读取到当前线程池的运行状态以及参数,如下图所示:

动态化线程池基于这几个接口封装了运行时状态实时查看的功能,用户基于这个功能可以了解线程池的实时状态,比如当前有多少个工作线程,执行了多少个任务,队列中等待的任务数等等。效果如下图所示:

面对业务中使用线程池遇到的实际问题,我们曾回到支持并发性问题本身来思考有没有取代线程池的方案,也曾尝试着去追求线程池参数设置的合理性,但面对业界方案具体落地的复杂性、可维护性以及真实运行环境的不确定性,我们在前两个方向上可谓“举步维艰”。最终,我们回到线程池参数动态化方向上探索,得出一个且可以解决业务问题的方案,虽然本质上还是没有逃离使用线程池的范畴,但是在成本和收益之间,算是取得了一个很好的平衡。成本在于实现动态化以及监控成本不高,收益在于:在不颠覆原有线程池使用方式的基础之上,从降低线程池参数修改的成本以及多维度监控这两个方面降低了故障发生的概率。希望本文提供的动态化线程池思路能对大家有帮助。

美团到店综合研发中心长期招聘前端、后端、数据仓库、机器学习/数据挖掘算法工程师,欢迎感兴趣的同学发送简历到:tech@meituan.com(邮件标题注明:美团到店综合研发中心-上海)

THE END
1.一文2000字入门性能测试!它涉及模拟真实世界中的用户行为、请求和负载,以便测量系统在不同条件下的响应时间、吞吐量、并发用户数和资源利用率等性能指标。 性能测试相关概念 并发:并发是指虚拟并发用户数,从业务角度,也可以理解为同时在线的用户数。 响应时间(Response Time,RT):响应时间是系统处理用户请求的时间。 https://blog.csdn.net/m0_60166861/article/details/144374821
2.1毫秒屏幕响应时间有什么用?华硕商城 在得物App发布了一条热门动态!快来围观,就等你啦!https://m.dewu.com/note/trend/details?id=253438965
3.如何评估中间件的性能和质量?评估中间件的标准有哪些?股票频道评估中间件的性能,首先要考虑其响应时间。响应时间是指从发送请求到收到响应所经过的时间。较短的响应时间意味着中间件能够快速处理请求,提供更流畅的服务。可以通过模拟大量并发请求来测试中间件在不同负载下的响应时间。 吞吐量也是一个关键指标。它表示单位时间内中间件能够处理的请求数量。高吞吐量意味着中间件能够https://stock.hexun.com/2024-12-06/216014901.html
4.Java通过响应时间和吞吐量mob64ca12f8a724的技术博客在processRequest方法中,我们使用System.currentTimeMillis()来记录请求处理的开始和结束时间。这将帮助我们计算每个请求的响应时间。 步骤4: 计算响应时间 我们在每次请求处理完成后,计算响应时间并将其累加到totalTime中。在所有请求处理完毕后,我们计算平均响应时间。 https://blog.51cto.com/u_16213466/12776265
5.计算机性能入门:新手必读教程响应时间:指计算机对用户操作的响应速度。例如,打开一个应用程序需要的时间,或鼠标点击后到光标移动所需的时间。 吞吐量:指计算机在单位时间内能处理的任务数量。比如每分钟可以处理多少个网页请求。 可靠性:指计算机系统在运行过程中不出错的能力。如果系统频繁崩溃或数据丢失,则可靠性较低。 资源利用率:指计算机系统https://www.imooc.com/article/372069
6.存储性能基准测试方法与工具天翼云开发者社区IOMeter:一款老牌的免费开源测试磁盘性能的工具,可以测试I/O的传输率和平均I/O响应时间。支持Unix、Linux、Windows平台。 IOZone:一个文件系统的benchmark工具,可以测试不同操作系统中文件系统的读写性能。包括Read、write、re-read、re-write、read backwards、read strided、fread、fwrite、random read、pread、mmap、https://www.ctyun.cn/developer/article/619805361881157
7.服务器最主要的三个性能参数,rpa,机器人,自动化文章还举例说明了如何根据不同的指标评估 一:Qps二:Tps三:Rt(响应时长)四:Load(系统负载)原理:每天80%的访问集中在20%的时间里,这20%时间叫做峰值时间。 公式:( 总PV数 * 80% ) / ( 每天秒数 * 20% ) = 峰值时间每秒请求数(QPS) 。 机器:峰值时间每秒QPS / 单台机器的QPS 在blog.csdn.net上https://wdlinux.cn/html/yingjian/20241205/22079.html
8.秒懂Requests响应时间获取技巧!虽然说也能很方便的获取到响应时间,但是还是要写额外的代码,其实 Requests 本身自带了计时器,每个请求都会记录请求的响应时间,话不多说,直接上代码,响应对象中,有一个elapsed属性,可以直接输出它来获取响应时间。 import requests r = requests.get("http://www.python.org") https://zhuanlan.zhihu.com/p/10999125155
9.不同类型负载对无触点稳压器响应时间的具体影响有多大不同类型负载对无触点稳压器响应时间的影响如下:阻性负载 影响较小:阻性负载的电流与电压相位基本一致,电压变化时,负载电流的变化相对较为平稳,不会产生额外的电动势或电流冲击来干扰稳压器的工作。例如,电阻炉、白炽灯等阻性负载接入无触点稳压器时,稳压器能够相对快速地对输入电压变化做出响应,调整输出电压,https://baijiahao.baidu.com/s?id=1818010065402326866&wfr=spider&for=pc
10.检测仪表知识3.分析仪表的基本的和主要的性能指标是(精度),(灵敏度)和(响应时间) 二.红外线分析器 1.所谓红外线是一种比可见光波长较长而又比微波波长较短的(电磁波),其波长约为(0.76μm)到(420μm)范围内,波长小于(2.5μm)称近红外线,波长为(2.5--25μm)为中红外线,波长大于(25μm)的称远红外线.红外线气体https://www.eepw.com.cn/article/201701/336033.htm
11.软件测试工作总结15篇总结是对某一阶段的工作、学习或思想中的经验或情况进行分析研究的书面材料,它可以帮助我们总结以往思想,发扬成绩,让我们抽出时间写写总结吧。那么你真的懂得怎么写总结吗?以下是小编为大家整理的软件测试工作总结,仅供参考,希望能够帮助到大家。 软件测试工作总结1 https://mip.wenshubang.com/gongzuozongjie/2966962.html
12.字节上岸成功,整理一波测试开发岗的基础知识,含答案功能:1.引入日志;2.函数执行时间统计;3.执行函数前预备处理;4.执行函数后清理功能;5.权限校验;6.缓存 python 装饰器@staticmethod和@classmethod区别和使用 @classmethod:类方法,类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法https://maimai.cn/article/detail?fid=1679089730&efid=YLyF0ejo0qEtjVB1UUE-yw
13.软件测试试题库(通用7套)5、高频集成:高频集成测试是指同步于软件开发过程,每隔一段时间对开发团队的现有代码进行一次集成测试。 6、分层集成、分布式集成、基于路径、功能、进度、风险、事件、使用等的集成等13种。 4. 恢复性测试属于软件测试的哪个阶段?并试阐述恢复性测试的概念和进行恢复性测试分析时主要应考虑的问题。 https://www.unjs.com/zuixinxiaoxi/ziliao/20170720000008_1398848.html
14.煤矿培训考试用题143、安全监控系统具有中心站手动遥控断电/复电功能,且断电/复电响应时间不大于30s。 144、瓦斯矿井必须装备煤矿安全监控系统。煤矿安全监控系统必须24h连续运行。 145、安全监控系统具有甲烷浓度超限报警和断电/复电控制功能以及数据实时存储功能。 146、采煤工作面采用串联通风时,必须在工作面进风巷道口以里10-15m处https://www.mkaq.org/html/2010/12/16/69111.shtml
15.消防知识题库防火题库(多选225题)D、响应时间指数(RTI) E、备用品数量 17、湿式系统开启末端试水装置的试水阀,(ABC)应动作。 A、报警阀 B、水流指示器 C、压力开关 D、消火栓泵 E、消防广播 18、报警阀动作后(ABCDE)。 A、水力警铃应鸣响 B、距水力警铃 3m 远处的声压级不应低于70dB https://www.safehoo.com/Item/5660537_3.aspx
16.拿offer必须掌握的SpringCloud面试题(含答案)5、什么是服务熔断和服务降级? 熔断机制是应对雪崩效应的一种微服务链路保护机制。当某个微服务不可用或者响应时间太长时,会进行服务降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的https://www.jianshu.com/p/eb8396ca1a44
17.热电偶与热电阻有哪些?区别是什么?如何才能正确选择和使用?4.注意环境因素:热电传感器容易受到外界环境的影响,如氧化、腐蚀、振动等,因此需要注意测量环境的温度、湿度和材料的性能稳定性等因素。 四、结论 热电偶和热电阻都是常用的温度传感器,它们的适用范围、性能稳定性、响应时间等方面有一定的区别。在选择和使用时,需要根据实际需求进行综合考虑。正确的安装和维护对于保证https://m.elecfans.com/article/2279951.html
18.浅谈N卡设置游戏延迟灰阶响应时间,影响更大的是观感,追求低拖影与低伪影的平衡。 另外就是黑白响应时间,也就是像素点在全亮与全暗之间的切换,VA的黑白响应时间普遍偏高。三星的两款VA显示器最低和平均响应时间甚至已经超过了很多TN面板,但是最长响应时间还是有点拖后腿了,体验就是黑色物体拖影会略多一些。 http://www.360doc.com/content/21/1006/00/60764982_998420756.shtml
19.测试开发:你所需要掌握了解的性能测试知识腾讯云开发者社区未达到系统瓶颈:随着并发用户数的增加,系统吞吐量会逐渐增加,此时响应时间会较快。 达到系统瓶颈:随着并发用户数的增加,系统吞吐量不再会增加,此时响应时间会开始变长。 超过系统瓶颈:随着并发用户数的增加,系统吞吐量出现下降,此时响应时间会逐渐拉长,甚至无响应。 https://cloud.tencent.com/developer/article/1850088
20.OLED屏幕的平板,到底有哪些好处?平板电脑4、屏幕响应时间快 这个很好理解,OLED屏幕的响应时间要优于绝大多数LCD屏幕。 5、动态画面显示具有优势 OLED屏幕玩游戏的时候,基本不会出现像LCD屏幕一样的残影和模糊的现象,在动态画面显示中天然比LCD更具优势。 6、屏幕厚度更薄 LCD屏幕的手机,基本都很厚,这是因为LCD本身屏幕厚,层数多。OLED不一样,屏幕可以做https://post.smzdm.com/p/agqz4857/
21.什么是一次调频二次调频?工控课堂火电机组转速不等率应为4%~5%,该技术指标不计算调频死区影响部分。该指标一般作为逻辑组态参考应用,机组 实际不等率需根据一次调频实际动作进行动态计算。 2) 调频死区: 机组参与一次调频死区应不大于|±0.033| Hz 或|±2| r/min。 3)快速性: 机组参与一次调频的响应时间应小于 3s。燃煤机组达到 75%目标负荷的https://www.shangyexinzhi.com/article/4362816.html
22.Java中GC的原理是什么大多数情况下对 Java 程序进行GC调优, 主要关注两个目标:响应速度、吞吐量 响应速度(Responsiveness)响应速度指程序或系统对一个请求的响应有多迅速。比如,用户订单查询响应时间,对响应速度要求很高的系统,较大的停顿时间是不可接受的。调优的重点是在短的时间内快速响应 https://www.chenxunyun.com/help/help_info_446.html