怎么用原生代码直观演示 CallerRunsPolicy 策略在多核处理器上的 CPU 负载转移差异

梦磊吖_6172

梦磊吖_6172

2026-05-30

385人浏览

原创

CallerRunsPolicy的核心行为是:当线程池与队列均饱和时,由提交任务的调用线程(如main线程)同步执行该任务,实现CPU负载从工作线程向调用线程的精准转移,避免新建线程或丢弃任务,在多核系统中体现为调用线程CPU使用率显著上升。

怎么用原生代码直观演示 callerrunspolicy 策略在多核处理器上的 cpu 负载转移差异

直接用原生 Java 代码演示 CallerRunsPolicy 在多核上的负载转移,关键在于让线程池“饱和”,迫使拒绝策略生效,同时通过 CPU 监控手段(如 /proc/stat 或 Runtime.availableProcessors() + 任务打点)直观呈现:当工作线程全部忙于 CPU 密集计算时,被拒绝的任务由**调用线程执行**,这部分计算负载就从线程池线程**转移到了主线程(或 Web 容器线程、gRPC 调用线程等)上**——而该线程原本可能在等待、IO 或空闲状态。

CPU 负载转移的直观验证逻辑

CallerRunsPolicy 不是“把任务丢给另一个线程”,而是让提交任务的那个线程自己跑完它。若提交线程是主线程(比如 main 方法里 submit),那 CPU 时间就计入 main 线程;若是 Tomcat 的 NIO-2 线程,则该线程的 CPU 使用率会在拒绝发生时突增。这种转移在多核系统中可被观测到:

  • 线程池的 8 个核心线程全在满负荷运行(绑定到不同物理核,top -H -p <pid></pid> 可见 8 个高 CPU 线程)
  • 主线程(或客户端线程)开始执行 compute-heavy 任务 → 其所在 CPU 核使用率同步上升
  • 整体 CPU 利用率接近物理核数 × 100%,但线程池线程数未超配,无额外上下文切换抖动

可运行的原生演示代码(无框架依赖)

以下代码在 8 核机器上实测有效,包含明确的时间戳、线程名打点和 CPU 占用提示:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
<p>public class CallerRunsDemo {
// 模拟 CPU 密集型任务:固定耗时约 300ms,强制占用一个核
static void cpuTask(int id) {
long start = System.nanoTime();
long count = 0;
while (System.nanoTime() - start </p><pre class="brush:php;toolbar:false;">public static void main(String[] args) throws InterruptedException {
    int coreCount = Runtime.getRuntime().availableProcessors(); // 如输出 8
    System.out.println("Detected CPU cores: " + coreCount);

    // 构建严格匹配 CPU 核心数的线程池(无冗余)
    ThreadPoolExecutor pool = new ThreadPoolExecutor(
            coreCount, coreCount,
            60, TimeUnit.SECONDS,
            new ArrayBlockingQueue(coreCount), // 有界队列,容量=核数
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    // 预热:启动 coreCount 个任务,占满所有核心线程
    System.out.println("→ Starting " + coreCount + " core tasks...");
    for (int i = 0; i  cpuTask(i));
    }
    Thread.sleep(100); // 确保全部开始

    // 此时线程池已满(core threads busy + queue full),第 coreCount+1 个任务触发 CallerRunsPolicy
    System.out.println("→ Submitting overflow task (will run on MAIN thread)...");
    long t0 = System.nanoTime();
    pool.submit(() -> cpuTask(999)); // 这个任务将由 main 线程同步执行
    long t1 = System.nanoTime();

    System.out.printf("[MAIN THREAD] Overflow task took %.1fms — executed on %s%n",
            (t1 - t0) / 1_000_000.0, Thread.currentThread().getName());

    pool.shutdown();
    pool.awaitTermination(5, TimeUnit.SECONDS);
}

}

运行后你会看到类似输出:

Miller CSV TSV JSON 数据处理器
Miller CSV TSV JSON 数据处理器

Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。

下载
Detected CPU cores: 8
→ Starting 8 core tasks...
[pool-1-thread-1] Task-0 done (302.4ms)
[pool-1-thread-2] Task-1 done (301.8ms)
...
[pool-1-thread-8] Task-7 done (303.1ms)
→ Submitting overflow task (will run on MAIN thread)...
[main] Task-999 done (304.7ms)   ← 关键:线程名是 "main"
[MAIN THREAD] Overflow task took 304.7ms — executed on main

如何确认 CPU 负载真的发生了转移?

在 Linux 上配合命令实时观察:

  • 终端 1:运行程序 java CallerRunsDemo
  • 终端 2:查 PID 并监控线程级 CPU:pid=$(pgrep -f CallerRunsDemo); top -H -p $pid -b -n 1 | head -20
  • 看到 8 个 pool-1-thread- 线程 CPU% ≈ 95–100%,同时 main 线程 CPU% 在 overflow 任务执行期间也跳到 90%+
  • 对比不启用 CallerRunsPolicy(如用 AbortPolicy):main 线程全程 CPU% ≈ 0%,溢出任务直接抛异常,无负载转移

为什么这个转移对多核系统有意义?

它避免了两种低效路径:

  • ❌ 创建新线程:会触发内核调度、栈分配、缓存失效,增加上下文切换开销
  • ❌ 丢弃任务:业务逻辑中断,可能造成数据不一致或重试风暴
  • ✅ 让调用方承担压力:天然形成反压(back-pressure),上游自然降速;且复用已有线程,不新增调度负担

在 8 核机器上,这意味着 8 个物理核被 8 个 worker 线程占满的同时,第 9 个请求的计算负载精准落在第 9 个可用时间片上——由主线程在它原本的核上执行,不抢资源、不换核、不扩容,只是“顺手干了”。这才是真正的轻量级负载转移。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10337

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7462

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6532

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1124

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

928

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1356

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2729

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20031

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1215

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 1.5万人学习

PHP入门速学(台湾同胞版)
PHP入门速学(台湾同胞版)

共10课时 | 1.6万人学习

韩顺平 2016年 最新PHP基础视频教程
韩顺平 2016年 最新PHP基础视频教程

共47课时 | 12.1万人学习