导读:本期聚焦于小伙伴创作的《如何利用 Executors.newWorkStealingPool 实战掌握 ForkJoin 线程池的变量窃取逻辑》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何利用 Executors.newWorkStealingPool 实战掌握 ForkJoin 线程池的变量窃取逻辑》有用,将其分享出去将是对创作者最好的鼓励。

Executors.newWorkStealingPool是Java 8为简化ForkJoinPool创建提供的工具方法,它默认会根据当前设备的CPU核心数创建对应数量的并行线程,底层基于ForkJoinPool实现,核心特性就是工作窃取,也就是空闲线程会从其他忙碌线程的任务队列尾部窃取任务来执行,以此提升整体任务处理效率。

Executors.newWorkStealingPool 基本用法

该方法有两个重载形式,无参版本会自动获取运行时可用的处理器核心数作为并行级别,带参版本可以手动指定并行级别。创建得到的线程池是ForkJoinPool的实例,因此可以直接提交ForkJoinTask类型的任务,也可以提交普通的Runnable或Callable任务。

下面是一个简单的使用示例,提交一个Callable任务并获取返回结果:

import java.util.concurrent.*;

public class WorkStealingPoolDemo {
    public static void main(String[] args) throws Exception {
        // 创建默认并行级别的ForkJoin线程池
        ExecutorService executor = Executors.newWorkStealingPool();
        // 提交普通Callable任务
        Future<String> future = executor.submit(() -> {
            Thread.sleep(1000);
            return "任务执行完成,执行线程:" + Thread.currentThread().getName();
        });
        // 获取任务结果
        System.out.println(future.get());
        executor.shutdown();
    }
}

ForkJoin 工作窃取的核心逻辑

ForkJoinPool中的每个工作线程都有自己的双端任务队列,线程正常执行任务时会从自己队列的头部取任务执行。当某个线程的任务队列为空时,它就会成为窃取线程,去其他线程的任务队列尾部窃取任务,这样能减少线程之间的竞争,因为普通线程取任务从头部,窃取线程取任务从尾部,操作的位置不同。

工作窃取的优势在于,当任务可以拆分成多个子任务时,拆分出的子任务会放到当前线程的队列中,其他空闲线程可以窃取这些子任务执行,充分利用多核CPU的计算能力,避免部分线程忙碌部分线程空闲的情况。

实战验证工作窃取过程

我们可以通过自定义ForkJoinTask,在任务执行时打印线程信息,同时构造多个不同耗时的任务,观察是否有线程窃取了其他线程的任务。下面的示例会创建多个耗时不同的计算任务,提交到线程池后,观察线程的执行情况。

import java.util.concurrent.*;

public class WorkStealVerify {
    // 自定义ForkJoinTask,打印执行线程和任务信息
    static class CustomTask extends RecursiveTask<Integer> {
        private final int taskId;
        private final int costTime;

        public CustomTask(int taskId, int costTime) {
            this.taskId = taskId;
            this.costTime = costTime;
        }

        @Override
        protected Integer compute() {
            String threadName = Thread.currentThread().getName();
            System.out.println("任务" + taskId + "开始执行,执行线程:" + threadName + ",预计耗时:" + costTime + "ms");
            try {
                Thread.sleep(costTime);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("任务" + taskId + "执行完成,执行线程:" + threadName);
            return taskId;
        }
    }

    public static void main(String[] args) throws Exception {
        // 创建并行级别为2的ForkJoin线程池,方便观察窃取过程
        ForkJoinPool pool = (ForkJoinPool) Executors.newWorkStealingPool(2);
        // 提交4个任务,前两个耗时短,后两个耗时长
        CustomTask task1 = new CustomTask(1, 200);
        CustomTask task2 = new CustomTask(2, 200);
        CustomTask task3 = new CustomTask(3, 1000);
        CustomTask task4 = new CustomTask(4, 1000);
        pool.invoke(task1);
        pool.invoke(task2);
        pool.invoke(task3);
        pool.invoke(task4);
        // 等待所有任务执行完成
        pool.awaitQuiescence(3, TimeUnit.SECONDS);
        pool.shutdown();
    }
}

运行上述代码后,你会发现两个工作线程会先执行任务1和任务2,执行完成后,这两个线程会去窃取任务3和任务4来执行,原本任务3和任务4可能在其中一个线程的队列中,另一个空闲线程会窃取队列尾部的任务,最终四个任务都会被两个线程执行完成,这就是工作窃取的实际表现。

注意事项

  • Executors.newWorkStealingPool创建的线程池是ForkJoinPool的实例,适合处理可以拆分的CPU密集型任务,不适合处理大量IO阻塞任务,否则会阻塞工作线程,降低窃取效率。
  • 提交到该线程池的任务如果是ForkJoinTask类型,拆分出的子任务会放到当前线程的队列中,更容易触发工作窃取,普通Runnable或Callable任务也会进入线程队列,但拆分逻辑需要开发者自己实现。
  • 不要随意设置过高的并行级别,并行级别超过CPU核心数反而会增加线程上下文切换的开销,一般默认使用无参方法即可。

总结

Executors.newWorkStealingPool是ForkJoinPool的便捷封装,其核心价值就是内置了工作窃取机制,通过空闲线程窃取忙碌线程的任务队列尾部任务,提升多核CPU的利用率。通过实际的代码示例,我们可以清晰观察到工作窃取的发生过程,在后续开发中,处理大批量可拆分的并发任务时,可以优先考虑使用该线程池来提升任务处理效率。

Executors.newWorkStealingPoolForkJoinPool工作窃取线程池并发编程修改时间:2026-07-10 21:30:29

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。