背景:
资源紧张的大环境下,懂的都懂。实现这个目标,我们不需要任何第三方库,使用JDK原生的 Runtime 类即可获取CPU核心数,并利用数学计算控制线程的"忙碌"与"休眠"的比例,从而达到精确控制CPU使用率的目的,控制的是整体利用率,不是这个程序的,这点很重要。
思路:
开发一个纯 Java 、无需第三方 Jar 包 、在 Linux 环境下运行的程序,能够将服务器的总 CPU 使用率精准控制在指定值(如 80%),并持续运行指定时间。
- 获取核心数 :使用
Runtime.getRuntime().availableProcessors()。 - 创建线程:根据核心数创建对应数量的线程,确保每个核心都有一个线程在工作。
- 计算占比:为了达到 80% 的使用率,我们需要让线程在一个总周期内,80% 的时间在执行计算(空转),20% 的时间在休眠。
- 执行控制 :
- 记录开始时间。
- 执行高强度的空运算。
- 如果运行时间超过了设定的"工作时间"(例如 80ms),则休眠剩余的时间(例如 20ms)。
- 打印输出信息:
操作:
直接上源代码,无侵入,使用的都是JDK自带的工具类,不会引入漏洞,另仅消耗CPU,内存几乎不耗。
CpuLoadSimulator.java
bash
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CpuLoadSimulator {
private static volatile boolean isRunning = false;
private static double TARGET_CPU_LOAD = 80.0;
private static int RUN_TIME_SECONDS = 60;
// --- 优化参数 ---
private static final long BIG_STEP_MS = 5; // 大调整步长 (误差大时用)
private static final long SMALL_STEP_MS = 1; // 小调整步长 (微调时用)
private static final double DEAD_ZONE = 2.0; // 死区范围 (+/- 2.0% 内不调整)
public static void main(String[] args) throws InterruptedException {
// 1. 参数解析
if (args.length > 0) {
try { TARGET_CPU_LOAD = Double.parseDouble(args[0]); if(TARGET_CPU_LOAD<1) TARGET_CPU_LOAD=1; if(TARGET_CPU_LOAD>100) TARGET_CPU_LOAD=100; } catch (Exception e) {}
}
if (args.length > 1) {
try { RUN_TIME_SECONDS = Integer.parseInt(args[1]); if(RUN_TIME_SECONDS<1) RUN_TIME_SECONDS=1; } catch (Exception e) {}
}
// 2. 初始化
int coreCount = Runtime.getRuntime().availableProcessors();
System.out.println("CPU 核心数: " + coreCount);
System.out.println("目标负载: " + TARGET_CPU_LOAD + "%");
System.out.println("运行时间: " + RUN_TIME_SECONDS + " 秒");
String osName = System.getProperty("os.name").toLowerCase();
if (!osName.contains("linux")) {
System.err.println("错误:仅支持 Linux 系统。");
return;
}
// 3. 启动工作线程
List<Thread> threads = new ArrayList<>();
final SharedSleepTime sharedSleepTime = new SharedSleepTime(100L); // 初始 100ms 休眠
for (int i = 0; i < coreCount; i++) {
Thread worker = new Thread(() -> {
while (!isRunning) { Thread.yield(); }
while (isRunning) {
long startTime = System.currentTimeMillis();
long currentSleep = sharedSleepTime.get();
long workTarget = 100 - currentSleep;
while (isRunning && (System.currentTimeMillis() - startTime) < workTarget) { }
if (isRunning && currentSleep > 0) {
try { Thread.sleep(currentSleep); } catch (Exception e) { break; }
}
}
});
worker.setName("CpuWorker-" + i);
threads.add(worker);
worker.start();
}
Thread.sleep(500);
isRunning = true;
System.out.println("线程已就绪,开始软启动...\n");
// 4. 主控循环
int remainingSeconds = RUN_TIME_SECONDS;
boolean isWarmup = true;
int warmupCountdown = 5; // 预热5秒
// --- 新增:时间格式化 ---
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
while (remainingSeconds > 0) {
double systemIdle = getSystemIdleFromTop();
double currentLoad = (systemIdle >= 0) ? (100.0 - systemIdle) : 100.0;
long currentSleep = sharedSleepTime.get();
long newSleep = currentSleep;
String status = "";
if (isWarmup) {
// 软启动逻辑
status = "软启动";
warmupCountdown--;
long targetSleep = (long) (100 - TARGET_CPU_LOAD);
if (currentSleep > targetSleep) {
newSleep = currentSleep - BIG_STEP_MS;
} else {
newSleep = targetSleep;
}
if (warmupCountdown <= 0 && systemIdle >= 0) {
isWarmup = false; // 预热结束
}
} else {
// 稳态调节逻辑
status = "稳调节";
double error = currentLoad - TARGET_CPU_LOAD;
if (Math.abs(error) <= DEAD_ZONE) {
newSleep = currentSleep;
} else if (error > 0) {
newSleep = currentSleep + (error > 5.0 ? BIG_STEP_MS : SMALL_STEP_MS);
} else {
newSleep = currentSleep - (error < -5.0 ? BIG_STEP_MS : SMALL_STEP_MS);
}
if (newSleep > 99) newSleep = 99;
if (newSleep < 0) newSleep = 0;
}
sharedSleepTime.set(newSleep);
// --- 修改点:打印当前时间 ---
String currentTime = sdf.format(new Date());
System.out.printf("%s [%s] 剩余: %3ds | 当前CPU: %6.2f%% | 目标: %5.1f%% | 休眠: %2dms%n",
currentTime, status, remainingSeconds, currentLoad, TARGET_CPU_LOAD, newSleep);
Thread.sleep(1000);
remainingSeconds--;
}
System.out.println("\n时间到!停止...");
isRunning = false;
for (Thread t : threads) t.join();
System.out.println("程序已退出。");
}
static class SharedSleepTime {
private volatile long ms;
public SharedSleepTime(long ms) { this.ms = ms; }
public long get() { return ms; }
public void set(long ms) { this.ms = ms; }
}
private static double getSystemIdleFromTop() {
try {
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", "top -b -n 1 | grep \"Cpu(s)\" | awk '{print $8}'"});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine();
if (line != null && !line.isEmpty()) {
return Double.parseDouble(line.replace("%", "").trim());
}
process.destroyForcibly();
} catch (Exception e) { }
return -1.0;
}
}
代码解析
-
Runtime.getRuntime().availableProcessors():- 这是 Java 原生方法,返回 JVM 可用的逻辑处理器数量。如果你的 CPU 支持超线程,这里返回的是线程数(例如 8 核 16 线程,这里通常返回 16)。
-
控制比例 (
targetLoad):- 我们定义了一个
100ms的循环周期。 - 如果要 80% 的负载,代码逻辑就是:
跑满 80ms->睡觉 20ms->重复。 - 动态调整我们程序的休眠时间。
- 我们定义了一个
如果发现 CPU 总负载低于 80% \rightarrow→ 程序减少休眠(多干活),把负载顶上去。
如果发现 CPU 总负载高于 80% \rightarrow→ 程序增加休眠(少干活),把负载降下来。
这就像汽车的定速巡航:当前速慢了就踩油门,当前速快了就踩刹车。
无论服务器上有没有其他程序在跑,它都会通过动态调整自己的算力,强行把服务器的总 CPU 使用率"钉"在 80% 左右。
3.while ((System.currentTimeMillis() - startTime) < workTimeMs):
- 这个
while循环内部没有任何实际逻辑,这叫做"忙等待"。它会让 CPU 处于满负荷运转状态,从而产生负载。
4.Thread.sleep(sleepTimeMs):
- 这是让 CPU 闲置的方法。通过控制休眠的长短,我们就能控制 CPU 的总体使用率。
核心机制:
- 如何压测 :工作线程执行一个没有任何内容的空循环 (
while循环)。通过疯狂读取系统时间判断是否该结束循环,让 CPU 处于"满负荷空转"状态。 - 如何控速 :主线程每秒读取一次
top命令。- 如果负载低于目标:减少线程的休眠时间(多干活)。
- 如果负载高于目标:增加线程的休眠时间(少干活)。
使用说明:
bash
java CpuLoadSimulator 40 30
40:目标使用率
30:持续多久


进阶使用指南:
编制启动脚本,加入定时任务crontab,自动化运行,优化加了随机时间,可选择。
bash
#!/bin/bash
sleep $((RANDOM % 600)) # 随机延迟 0-10 分钟 (秒数)
cd /data/mdm2.0/script
nohup /usr/local/jdk/jdk8u462-b08/bin/java CpuLoadSimulator 70 180 >> nohup.out 2>&1 &
每隔30分钟,执行一次,设定目标服务器的CPU利用率:70%,持续时间:3分钟。
crontab -l
*/30 * * * * /data/mdm2.0/script/start.sh &> /dev/null

效果展示:


解惑:
1. 哪块代码在消耗 CPU?
bash
while (isRunning) {
long startTime = System.currentTimeMillis();
long currentSleep = sharedSleepTime.get();
long workTarget = 100 - currentSleep;
// ====== 这里是消耗 CPU 的地方! ======
while (isRunning && (System.currentTimeMillis() - startTime) < workTarget) {
// 空循环体
}
// =====================================
// 下面是休息,不消耗 CPU
if (isRunning && currentSleep > 0) {
try { Thread.sleep(currentSleep); } catch (Exception e) { break; }
}
}
2. 它是怎么消耗的?
这种消耗方式叫做 "忙等待"。原理如下:
- 疯狂地"询问"时间 :
System.currentTimeMillis()是一个去操作系统读取当前时间的函数。 - 无意义地做数学题 :
程序在while循环里,每一毫秒都要执行几十万甚至几百万次:- 读取当前时间。
- 减去开始时间。
- 比较是否到了目标时间。
- 跳回循环开头。
- 不让 CPU 休息 :
因为循环体里没有任何Thread.sleep()或I/O操作,CPU 根本没有机会把任务挂起。它必须全神贯注、一刻不停地执行这几行简单的代码。
3. 为什么不用更复杂的代码?
你可能会问:"为什么不写个死循环算圆周率 3.14159... 或者做矩阵乘法来消耗 CPU?"
- 简单高效:比较时间戳是一个非常轻量级的操作,对内存几乎零消耗,不会触发 Java 的垃圾回收(GC)。
- 纯 CPU 压力:我们的目的是测试 CPU 负载能力,而不是测试计算能力或内存能力。空循环能最大限度地榨干 CPU 的"指令周期",而不受内存带宽的瓶颈影响。
4. 稳态控制版- 算法优化
- 思路 :引入了 PID 控制(简化版) 和 软启动。
- 改进 :
- 软启动:启动时不让线程立刻满载,而是慢慢减少休眠时间,平滑爬升到目标负载,消除了启动突刺。
- 死区控制:设定一个范围(如目标 ±2%),在此范围内不调整,消除了震荡现象。