如何正确设置Java线程池参数?「建议收藏」

如何正确设置Java线程池参数?「建议收藏」如何正确设置Java线程池参数?前言:在上篇文章我已经给读者介绍了Java线程池的基本使用,以及参数的定义。你真的了解Java线程池参数的含义吗本文我们更进一步,来聊聊在实际的工作中如何设置Java线程池参数的。当我们自定义线程池的时候corePoolSize、maximum…

大家好,又见面了,我是你们的朋友全栈君。

                                      如何正确设置Java线程池参数?

 

     前言:在上篇文章我已经给读者介绍了Java线程池的基本使用,以及参数的定义。你真的了解Java线程池参数的含义吗

    本文我们更进一步,来聊聊在实际的工作中如何设置Java线程池参数的。

 

  当我们自定义线程池的时候 corePoolSize、maximumPoolSize、workQueue(队列长度)该如何设置?

  你以为我要给你讲分 IO 密集型任务或者分 CPU 密集型任务?

  当然这个教科书式的流程我们决不能少!

  1:IO密集型任务时:

《Java并发编程实战》一书中给出的计算方式是这样的:

如何正确设置Java线程池参数?「建议收藏」

 

2:CPU密集型任务:

可以把核心线程数设置为核心数+1。

为什么要加一呢?

《Java并发编程实战》一书中给出的原因是:即使当计算(CPU)密集型的线程偶尔由于页缺失故障或者其他原因而暂停时,这个“额外”的线程也能确保 CPU 的时钟周期不会被浪费。

看不懂是不是?没关系我也看不懂。反正把它理解为一个备份的线程就行了。

 

教科书的痛点:

 

我之前有个系统就是按照这个公式算出来的参数去配置的。

结果效果并不好,甚至让下游系统直呼受不了。

这个东西怎么说呢,还是得记住,面试的时候有用。真实场景中只能得到一个参考值,基于这个参考值,再去进行调整。

我们再看一下美团的那篇文章调研的现有解决方案列表:

如何正确设置Java线程池参数?「建议收藏」

第一个就是我们上面说的,和实际业务场景有所偏离。

第二个设置为 2*CPU 核心数,有点像是把任务都当做 IO 密集型去处理了。而且一个项目里面一般来说不止一个自定义线程池吧?比如有专门处理数据上送的线程池,有专门处理查询请求的线程池,这样去做一个简单的线程隔离。但是如果都用这样的参数配置的话,显然是不合理的。

第三个不说了,理想状态。流量是不可能这么均衡的,就拿美团来说,下午3,4点的流量,能和 12 点左右午饭时的流量比吗?

基于上面的这些解决方案的痛点,美团给出了动态化配置的解决方案。

 

动态更新的工作原理是什么?

如何正确设置Java线程池参数?「建议收藏」

 

在运行期线程池使用方调用此方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。

对于当前值小于当前工作线程数的情况,说明有多余的worker线程,此时会向当前idle的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收;

对于当前值大于原始值且当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务,setCorePoolSize具体流程

 

因此动态更新线程参数的核心在于:

setCorePoolSize   setMaxNumPoolSize  以及重新设置队列长度三个方法。

由于美团的文章发表只是理论上的概念并未发布源码。

 

因此参考美团文章给出的思路我来尝试实现微服务的动态更新线程池参数的Stater.

1:新建一个动态调整线程池参数的Stater,命名为 iread-threadfactory

如何正确设置Java线程池参数?「建议收藏」

2: 由于需要调整最大线程数、核心线程数、队列长度三个参数,因此将三个参数做成可配置的,又因为需要辨别每个线程,因此还需要设置线程池的名字。因此建立如下配置类:

WoreadThreadFactoryProperties

package com.cn.woread.configuration;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "woread.thread")
public class WoreadThreadFactoryProperties {
	
	
	
	
	List<Properties>list=new ArrayList<Properties>();

	public List<Properties> getList() {
		return list;
	}

	public void setList(List<Properties> list) {
		this.list = list;
	}


	
	

	
	
	
	
	
	
	
	

}

Properties

package com.cn.woread.configuration;

public class Properties {
	
	
	private String threadFactoryName;
	
	//最大线程数
	private int maximumPoolSize;
	
	//核心线程数
	private int corePoolSize;
	
	//队列大小
	private int capacity;
	
	
	

	public String getThreadFactoryName() {
		return threadFactoryName;
	}

	public void setThreadFactoryName(String threadFactoryName) {
		this.threadFactoryName = threadFactoryName;
	}

	public int getMaximumPoolSize() {
		return maximumPoolSize;
	}

	public void setMaximumPoolSize(int maximumPoolSize) {
		this.maximumPoolSize = maximumPoolSize;
	}

	public int getCorePoolSize() {
		return corePoolSize;
	}

	public void setCorePoolSize(int corePoolSize) {
		this.corePoolSize = corePoolSize;
	}

	public int getCapacity() {
		return capacity;
	}

	public void setCapacity(int capacity) {
		this.capacity = capacity;
	}

}

 

3:创建线程池创建处理类:WoreadThreadPoolExecture

package com.cn.woread.configuration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;

public class WoreadThreadPoolExecture {

	
	private WoreadThreadFactoryProperties properties;
	
	private static Map<String,ThreadPoolExecutor> threadFactorys=new HashMap<String,ThreadPoolExecutor>();
	
	
	public WoreadThreadPoolExecture(WoreadThreadFactoryProperties properties) {
		this.properties=properties;
	}


	public ThreadPoolExecutor reBulidThreadFactory(String name) {
		List<Properties >list=properties.getList();
		ThreadPoolExecutor threadFactory=null;
		if(list!=null&&list.size()!=0) {
			Optional<Properties> mapOpt=list.stream().filter(l->name.equals(l.getThreadFactoryName())).findFirst();
			int maximumPoolSize=Runtime.getRuntime().availableProcessors() +1;
			int corePoolSize=maximumPoolSize;
			int capacity=1000;
			
			if(mapOpt.isPresent()) {
				Properties map=mapOpt.get();
				//最大线程数 
				 maximumPoolSize=map.getMaximumPoolSize();
				//核心线程数
				 corePoolSize=map.getCorePoolSize();
				//队列大小
				 capacity=map.getCapacity();
			}
			if(threadFactorys.containsKey(name)) {
				threadFactory=threadFactorys.get(name);
				threadFactory.setCorePoolSize(corePoolSize);
				threadFactory.setMaximumPoolSize(maximumPoolSize);
				WoreadLinkedBlockingQueue queue=(WoreadLinkedBlockingQueue)threadFactory.getQueue();
				queue.setCapacity(capacity);
				threadFactory.prestartAllCoreThreads();
			}else {
				threadFactory=new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 60, TimeUnit.SECONDS, new WoreadLinkedBlockingQueue<Runnable>(capacity));
				threadFactorys.put(name, threadFactory);
			}
			
		}
		return threadFactory;
	}
	
	
	

}

该类只有一个方法:ThreadPoolExecutor reBulidThreadFactory(String name)

根据线程池名字创建相应参数的线程池(如果从未创建)

根据线程池名字创建相应参数的线程池(如果已经创建—实现动态调整参数的需求)

    private static Map<String,ThreadPoolExecutor> threadFactorys=new HashMap<String,ThreadPoolExecutor>();
 

   利用一个静态的map存储所有创建的线程池对象
      1          threadFactory.setCorePoolSize(corePoolSize);
      2          threadFactory.setMaximumPoolSize(maximumPoolSize);
      3          WoreadLinkedBlockingQueue queue=(WoreadLinkedBlockingQueue)threadFactory.getQueue();
      4         queue.setCapacity(capacity);
      5         threadFactory.prestartAllCoreThreads();

  1-2行代码利用配置文件配置的线程数量来重新设置线程参数,可是却未找到重新设置队列长度的方法,通过翻看源码发现,

 队列长度capacity被设置成了final对象,不可更改,因此我的做法是重写队列,将大小设置为可改变的,提供改变方法

 创建 线程队列类:WoreadLinkedBlockingQueue

  如何正确设置Java线程池参数?「建议收藏」

4:创建staer类WoreadThreadFactoryConfiguration

package com.cn.woread.stater;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.cn.woread.configuration.WoreadThreadFactoryProperties;
import com.cn.woread.configuration.WoreadThreadPoolExecture;

@Configuration
@EnableConfigurationProperties(WoreadThreadFactoryProperties.class)
public class WoreadThreadFactoryConfiguration {
	
	@Autowired
	private WoreadThreadFactoryProperties properties;
	
	@Bean
	@ConditionalOnMissingBean(WoreadThreadPoolExecture.class)
	public WoreadThreadPoolExecture woreadThreadPoolExecture() {
		return new WoreadThreadPoolExecture(properties);
	}
	

}

至此动态调整参数的线程池stater构建完毕。

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139634.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 字符串长度函数strlen()。。字符串函数头文件string.h「建议收藏」

    百度知道看到的string.h头文件里常用的函数有:strlen求字符串长度。strcmp比较2个字符串是否一样。strcat字符串连接操作。strcpy字符串拷贝操作。strncat字符串连接操作(前n个字符)。strncpy字符串拷贝操作(前n个字符)。strchr查询字串。strstr查询子串。string.h是C语言里面关于字符数组的函数定义的头文件,更详细的可以到i…

    2022年4月8日
    49
  • 近两万字小程序攻略发布了

    近两万字小程序攻略发布了

    2021年6月12日
    135
  • RabbitMQ默认端口

    4369:epmd,RabbitMQ节点和CLI工具使用的对等发现服务5672、5671:由不带TLS和带TLS的AMQP0-9-1和1.0客户端使用25672:用于节点间和CLI工具通信(Erlang分发服务器端口),并从动态范围分配(默认情况下限制为单个端口,计算为AMQP端口+20000)。除非确实需要这些端口上的外部连接(例如,群集使用联合身份验证或在子网外部的计算机上使用CLI工具),否则这些端口不应公开。有关详细信息,请参见网络指南。35672-35682:由CLI工具..

    2022年4月5日
    161
  • servlet-Filter过滤器

    servlet-Filter过滤器Filter过滤器Filter过滤器是javaweb的三大组件之一,三大组件分别是:Servlet程序,Listener监听器,Filter过滤器Filter过滤器它是javaEE的规范,也就是接口Filter过滤器它的作用是拦截请求,过滤响应拦截请求常见的应用场景:权限检查日记操作事务管理等等原理package at.guitu.com.FIlter;import javax.servlet.FilterChain;import javax.servlet.Filte

    2022年8月8日
    8
  • cockpit二次开发_laravel api

    cockpit二次开发_laravel api背景:最近公司要基于cockpit,来定制自己的一个服务器管理web应用。嗯。。cockpit是啥?能干嘛?我要拿它干嘛?如你所见,我此刻是懵逼的。cockpit了解我熟练的打开了百度又打开了bing哦吼,二度懵逼。经过几番了解,大概是知道了LinuxCockpit是一个基于Web界面的应用,它提供了对系统的图形化管理。因为功能集成,对服务器管理来说,可以称得上是神器,深受linux开发者的喜爱。(呵呵。。)最后我大概是知道了,公司就是想让我在人..

    2025年7月27日
    4
  • for while循环语句举例python_python中while和for循环的用法

    for while循环语句举例python_python中while和for循环的用法程序在一般情况下是按顺序执行的。编程语言提供了各种控制结构,允许更复杂的执行路径。循环语句允许我们执行一个语句或语句组多次,下面是在大多数编程语言中的循环语句的一般形式1.循环控制语句在了解循环语句的使用方法之前,我们先来了解几个循环控制语句:1)…

    2022年9月25日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号