当前位置: 主页 > JAVA语言

java线程池最大线程数-java线程池执行完毕

发布时间:2023-02-13 16:03   浏览次数:次   作者:佚名

文章目录

前言

线程池在 Java 服务中无处不在,但确切的线程数是合适的通常是一个意见问题。在这里,总结一下我看到的观点,并根据我的个人经验做一些总结。

一、经典方法

摘自 Java 并发编程的实际应用

java线程池最大线程数_java线程池_java线程池执行完毕

二、要点分析 1.确定 CPU 的数量

int N_CPUS = Runtime.getRuntime().availiableProcessors();

此代码是 JDK 提供的解决方案。通常,它在物理机器上是准确的。但是,在云原生/虚拟化环境中,笔者遇到了获取结果数量多于实际数量的情况,导致线程过多。所以代码可以这样写,实际操作还是需要确认的。另外,所谓的可用处理器,其实都是可用的处理器内核,毕竟可能只有1个处理器,但有8个内核。

2. 确定任务类型

工程实践中的任务类型可能是I/O密集型的,可能是计算密集型的,也可能是两者的混合,即混合任务。虽然好的设计应该是将I / O过程和计算过程分开的设计,但也有一些遗留系统具有许多非常规逻辑,由于各种历史原因,将两者组合在一个线程中。因此java线程池最大线程数,根据经验确定的任务类型可能不准确。最终,通过等待/计算比率来衡量任务的类型更为合理。作为开发人员,我们仍然需要与数据对话。所以让这个逻辑连续运行几次,你可以看到区别。以下命令和结果是 CentOS。

查看线程状态

tpid 是线程 ID,这里以 grpc boss 事件循环线程为例

cat /proc/${tpid}/status

cat /proc/27792/status
Name:	grpc-nio-boss-E
Umask:	0022
State:	S (sleeping)
Tgid:	27738
Ngid:	0
Pid:	27792
PPid:	1
TracerPid:	0
Uid:	18930	18930	18930	18930
Gid:	1001	1001	1001	1001
FDSize:	256
Groups:	1001 3007 3030 
VmPeak:	 2958948 kB
VmSize:	 2958944 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	  550244 kB
VmRSS:	  550244 kB
RssAnon:	  536192 kB
RssFile:	   14052 kB
RssShmem:	       0 kB
VmData:	 2795616 kB
VmStk:	     132 kB
VmExe:	       4 kB
VmLib:	   19012 kB
VmPTE:	    1460 kB
VmSwap:	       0 kB
Threads:	53
SigQ:	0/14120
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000000000004
SigIgn:	0000000000000003
SigCgt:	2000000181005ccc
CapInh:	0000000000000000
CapPrm:	0000000000000000
CapEff:	0000000000000000
CapBnd:	0000001fffffffff
CapAmb:	0000000000000000
Seccomp:	0
Speculation_Store_Bypass:	vulnerable
Cpus_allowed:	3
Cpus_allowed_list:	0-1
Mems_allowed:	00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list:	0
# 线程自愿上下文切换
voluntary_ctxt_switches:	12127
# 线程非自愿上下文切换
nonvoluntary_ctxt_switches:	4

非自愿上下文切换 /

资源上下文切换非常大 表示线程经常主动放弃 CPU 时间片,通常是在等待其他资源,如 I/O 完成、等待锁定等。此处的单位是时间,可以是定性的,但不提供确切的运行/等待比率。

查看线程调度状态

PID:进程 ID,TPID:线程 ID

cat /proc/${pid or tpid}/sched


cat /proc/27792/sched
grpc-nio-boss-E (27792, #threads: 53)
-------------------------------------------------------------------
se.exec_start                                :   10308747122.791804
// 虚拟运行总时间(等待时间+使用CPU时间)
se.vruntime                                  :      52693327.134406
// 实际运行总时间(使用CPU时间)
se.sum_exec_runtime                          :          1285.444999
// 跨CPU核心切换次数
se.nr_migrations                             :                 2446
// 上下文切换总次数
nr_switches                                  :                12131
// 自愿切换次数
nr_voluntary_switches                        :                12127
// 非自愿切换次数
nr_involuntary_switches                      :                    4
se.load.weight                               :                 1024
policy                                       :                    0
// 线程优先级
prio                                         :                  120
clock-delta                                  :                   36
mm->numa_scan_seq                            :                    0
numa_migrations, 0
numa_faults_memory, 0, 0, 1, 0, -1
numa_faults_memory, 1, 0, 0, 0, -1

使用 se.se.vruntime,se.sum_exec_runtime,我们可以得到一个相对合理的等待/计算比率。

严格来说,这个比例不是那么准确。因为,如果线程的任务有很多条件分支,导致任务不同,则比率将大幅波动并失去其参考意义。结果java线程池最大线程数,糟糕的设计带来了无数的不确定性,限制了经验和理论的作用。

此时,我们可以应用公式并愉快地确定线程数。但是,现实可能有点复杂,所以让我们谈谈这些限制。

3. 工程限制 垂直限制

线程池中的任务依赖于其他下游资源。例如,连接池中的连接。假设线程池设置为 30,而连接池最多只允许 20 个,则仍有 10 个线程在等待,因为它们无法获取链接。再比如通过网络进行大批量数据传输,内核TCP窗口太小或者对方接收太慢,导致Socket发送缓存总是被填满,发送线程只能等待。

横向限制

JVM一次性内存不足以支持更多的并发性;通常进程中有多个线程池,有多个类似任务的线程池,线程优先级设置会影响最终的W/C比率。

总结

以上就是我今天要讲的,本文对设置线程数的方法、元素和工程限制做了一些个人总结,希望对大家有所帮助。如果你有更好的方法,也欢迎你射砖,谢谢你的阅读。