怎么调整线程大小最合适?
- 核心线程数、最大线程数是不是越大越好?
线程池的核心线程数(corePoolSize
)和最大线程数(maximumPoolSize
)的设定需根据任务类型 和系统资源动态调整,盲目调大可能引发性能问题甚至系统崩溃。
markdown
1. CPU密集型任务:线程数过多会引发频繁的上下文切换,消耗CPU资源。
2. IO密集型任务:线程数过大可能导致内存耗尽(每个线程占用栈内存)或触发拒绝策略。
- 案例分析:对比不同线程池配置的效果,配置的是否合理
案例1:CPU密集型任务(计算素数)
java
public class CpuIntensiveDemo {
private static final AtomicInteger counter = new AtomicInteger(0);
/**
* 固定任务数
*/
private static final int TASK_COUNT = 100000;
// 计算第5个素数
private static int calculatePrime(int n) {
int count = 0, num = 2;
while (count < n) {
boolean isPrime = true;
for (int i = 2; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
count++;
}
num++;
}
// 记录完成的任务数
counter.incrementAndGet();
return num - 1;
}
public static void main(String[] args) {
int logicalProcessors = Runtime.getRuntime().availableProcessors();
// 合理配置:线程数 = 逻辑处理器数(16)
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(logicalProcessors, logicalProcessors, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
// 错误配置:线程数 = 160(远超过逻辑处理器数)
ThreadPoolExecutor executor2 =
new ThreadPoolExecutor(160, 160, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Runnable task = () -> calculatePrime(5);
runTest("合理配置(16线程)", executor1, task, TASK_COUNT);
runTest("错误配置(160线程)", executor2, task, TASK_COUNT);
}
private static void runTest(String name, ThreadPoolExecutor executor, Runnable task, int taskCount) {
counter.set(0);
long start = System.currentTimeMillis();
for (int i = 0; i < taskCount; i++) {
executor.execute(task);
}
// 停止接受新任务
executor.shutdown();
try {
// 等待所有任务完成
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.printf("%s 总耗时:%d ms (完成数: %d)\n", name, end - start, counter.get());
}
}
- 运行结果:
scss
合理配置(16线程) 总耗时:50 ms (完成数: 100000)
错误配置(160线程) 总耗时:85 ms (完成数: 100000)
- 结论:线程数并非越大越好,当线程数超过硬件并行能力时,调度开销会显著降低性能。
案例2: IO密集型任务(模拟网络请求)
java
public class IoIntensiveDemo {
// 模拟IO密集型任务:模拟网络请求(睡眠代替IO等待)
private static void mockHttpRequest() {
try {
Thread.sleep(100); // 模拟IO等待100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 场景1:线程数过小(错误配置)
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 场景2:合理放大线程数(正确配置)
int logicalProcessors = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor2 = new ThreadPoolExecutor(logicalProcessors, logicalProcessors * 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 提交任务并统计执行时间
Runnable task = () -> mockHttpRequest();
runTest("错误配置(2线程)", executor1, task);
runTest("合理配置(" + logicalProcessors + "线程)", executor2, task);
}
private static void runTest(String name, Executor executor, Runnable task) {
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
executor.execute(task);
}
((ThreadPoolExecutor) executor).shutdown();
while (!((ThreadPoolExecutor) executor).isTerminated()) {
}
long end = System.currentTimeMillis();
System.out.printf("%s 总耗时:%d ms\n", name, end - start);
}
}
-
运行结果:
错误配置(2线程) 总耗时:5522 ms
合理配置(16线程) 总耗时:763 ms -
结论:IO密集型任务中,适当增大线程数可显著提升吞吐量。
调优建议
- 确定任务类型
- CPU密集型:计算、加密、压缩等,CPU使用率高。
- IO密集型:网络请求、文件读写、数据库操作等,CPU空闲率高。
- 调优核心原则
任务类型 | 线程数公式 |
---|---|
CPU密集型 | 线程数 ≈ 物理核心数(或逻辑线程数) |
IO密集型 | 线程数 ≈ 逻辑线程数 * 2 |
-
线程数更严谨的计算的方法(仅供参考,具体以业务实际情况为准)
最佳线程数 = N(CPU 核心数)∗(1+WT(线程等待时间)/ST(线程计算时间))
其中 WT(线程等待时间)= 线程运行总时间 - ST(线程计算时间) -
线程等待时间所占比例越高,需要越多线程。线程计算时间所占比例越高,需要越少线程