如何正确设置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构建完毕。

 

 

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • SqlConnection.ConnectionString 属性

    SqlConnection.ConnectionString 属性ConnectionString类似于OLEDB连接字符串,但并不相同。与OLEDB或ADO不同,如果“PersistSecurityInfo”值设置为false(默认值),则返回的连接字符串与用户设置的ConnectionString相同但去除了安全信息。除非将“PersistSecurityInfo”设置为true,否则,SQLServer.NETF

  • kali 目录扫描_kali扫描命令

    kali 目录扫描_kali扫描命令1、简介dirsearch是一个基于python3的命令行工具,常用于暴力扫描页面结构,包括网页中的目录和文件。相比其他扫描工具disearch的特点是:支持HTTP代理多线程支持多种形式的网页(asp,php)生成报告(纯文本,JSON)启发式检测无效的网页递归扫描用户代理随机化批量处理扫描器与字典(注:字典必须是文本文件)2、下载及安装GitHub的下载地址为:https://github.com/maurosoria/dirsearchWindows10安装方式点击c

  • linux下java的环境配置

    linux下java的环境配置linux下java的环境配置文章目录linux下java的环境配置1.删除原有的java环境2.去官网下载相应的Java环境3.在Linux上进行解压4.修改~/.bashrc参考链接之前在大数据配置hadoop开发环境的时候,进行了相关的配置,所以还有印象,接下来对虚拟机ubuntu进行java的环境配置1.删除原有的java环境2.去官网下载相应的Java环境我用的是java8的环境,比较经典,另外还有java11也是比较稳定的,相较于java8做了一些改进3.在Linux上进行解

  • 根据bak还原数据库,备份集中的数据库与现有数据库“XXX”数据库不同

    根据bak还原数据库,备份集中的数据库与现有数据库“XXX”数据库不同在做数据库相关的日常工作中,还原与备份数据库会经常遇到,有时候同样的sql2008备份的数据库,从别人那边备份的数据库文件,在自己的电脑上还原会出现:的错误。解决方法有两种:第一种:右键数据库点击还原数据库,填上需要还原的数据库名,就可以直接还原了。第二种:在新建的数据库上还原数据库时,选好备份文件后,勾选上覆盖现有数据库即可。原文地址:https://blog.csdn.net/sushena/…

  • pytest重试_pytest的conftest

    pytest重试_pytest的conftest安装:pip3installpytest-rerunfailures重新运行所有失败用例要重新运行所有测试失败的用例,请使用–reruns命令行选项,并指定要运行测试的最大次数:$py

  • PCB设计-Allegro软件入门系列第九讲-Class分类和Subclass应用

    PCB设计-Allegro软件入门系列第九讲-Class分类和Subclass应用在Allegro软件中,Class和SubcClass是一个相对新的专业术语,这里单独拿一节出来给大家讲解一下。相信不少画过PCB的读者也许跟笔者一样也用过AD,刚从AD过来学习allegro都会发现allegro这个平台所有对象都分Class和Subclas。比如上一节中的板框我是定义在了BoardGeometry的Outline里面。其实Allegro将所有元素都分类的很仔细是方便后…

发表回复

您的电子邮箱地址不会被公开。

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