为何服务器QPS上不去?
|
响。 所有线程池的工作方式本质是一样的: 有一个队列,任务被提交到这个队列中。一定数量的线程会从该队列中取任务,然后执行。 任务的结果可以发回客户端(比如应用服务器的情况下),或保存到数据库中,或保存到某个内部数据结构中,等等。但是在执行完任务后,这个线程会返回任务队列,检索另一个任务并执行,如果没有更多任务要执行,该线程会等待下一个任务。 线程池有最小线程数和最大线程数。池中会有最小数目的线程随时待命,等待任务指派给它们。因为创建线程的成本非常高昂,这样可以提高任务提交时的整体性能:已有的线程会拿到该任务并处理。另一方面,线程需要一些系统资源,包括栈所需的原生内存,如果空闲线程太多,就会消耗本来可以分配给其他进程的资源。最大线程数还是一个必要的限流阀,防止一次执行太多线程。 ThreadPoolExecutor 和相关的类将最小线程数称作核心池大小,如果有个任务要执行,而所有的并发线程都在忙于执行另一个任务,就启动一个新线程,直到创建的线程达到最大线程数。 设置最大线程数 对于给定硬件上的给定负载,最大线程数设置为多少最好呢? 这个问题回答起来并不简单;它取决于负载特性以及底层硬件。特别是,最优线程数还与每个任务阻塞的频率有关。 为方便讨论,假设 JVM 有 4 个 CPU 可用。我们的目标就是最大化这 4 个 CPU 的利用率。 很明显,最大线程数至少要设置为 4。的确,除了处理这些任务,JVM 中还有些线程要做其他的事,但是它们几乎从来不会占用一个完整的 CPU。如果使用的是并发垃圾收集器,这是个例外,后台线程必须有足够的 CPU 来运行,以免在处理堆这方面落后。 如果线程数多于 4,会有帮助吗?这时就要看负载特性了。考虑最简单的情况,假定任务都是计算密集型的:没有外部网络调用(比如不会访问数据库),也不会激烈地竞争内部锁。在使用模实体管理器(mock entity manager)的情况下,股价历史批处理程序就是一个这样的应用:实体上的数据完全可以并行计算。 下面就使用线程池计算一下 10,000 个模股票实体的历史,假设机器有 4 个 CPU,使用不同的线程数测试,具体的性能数据见表1。如果池中只有 1 个线程,计算数据集需要 255.6 秒;用 4 个线程,则只需要 77 秒。如果线程数超过 4 个,随着线程数的增加,需要的时间会稍多一些。 表1:计算 10,000 个模的价格历史所需时间程时,这列为 25%。但是这种完全线性的比例不可能出现,原因有这么几点:如果没有其他线程帮助,这些线程必须自己来协同,实现从运行队列中选取任务(一般而言,通常会有更多同步)。到了使用 4 个线程的时候,系统会 100% 消耗可用的 CPU,尽管机器可能没有运行其他用户级的应用,但是会有各种系统级的进程进来,并使用 CPU,从而使得 JVM 无法 100% 地使用所有 CPU 周期。 尽管如此,这个应用在伸缩性方面表现还不错,且即使池中的线程数被显著高估,性能损失也比较轻微。
不过在其他情况下,性能损失可能会很大。在 Servlet 版的股票 (编辑:常州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


