【高并发】Java 并行与串行深入解析:性能优化与实战指南

Java 并行与串行深入解析:性能优化与实战指南

在高性能应用开发中,我们常常会面临 串行(Serial)并行(Parallel) 的选择。串行执行任务简单直观,但并行能更高效地利用 CPU 资源,提高吞吐量。然而,并行并不总是比串行快,如何合理选择和优化并行任务,才是性能提升的关键。本文将深入解析 Java 并行与串行的原理 ,并结合 实战代码 带你掌握 高效的并行计算 方法。


1. 什么是串行与并行?

(1)串行(Serial)

串行指的是任务按顺序执行 ,一个任务执行完后,才会执行下一个任务。例如,以下代码模拟 读取数据库 → 计算数据 → 写入文件 的串行执行流程:

java 复制代码
public class SerialProcessing {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        readFromDatabase();  // 假设耗时 2s
        processData();       // 假设耗时 3s
        writeToFile();       // 假设耗时 2s
        System.out.println("总耗时: " + (System.currentTimeMillis() - start) + " ms");
    }

    static void readFromDatabase() { sleep(2000, "读取数据库"); }
    static void processData() { sleep(3000, "计算数据"); }
    static void writeToFile() { sleep(2000, "写入文件"); }

    static void sleep(int millis, String task) {
        try { 
            System.out.println(task + " 开始...");
            Thread.sleep(millis); 
            System.out.println(task + " 结束");
        } catch (InterruptedException e) {}
    }
}

执行结果

复制代码
读取数据库 开始...
读取数据库 结束
计算数据 开始...
计算数据 结束
写入文件 开始...
写入文件 结束
总耗时: 7000 ms

🚨 问题 :任务串行执行 ,总耗时 = 2s + 3s + 2s = 7s,如果任务更多,系统响应会非常慢。


(2)并行(Parallel)

并行执行任务意味着 多个任务同时运行 ,在多核 CPU 环境下能够显著提高效率。例如,我们可以使用 多线程并行执行任务

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ParallelProcessing {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        
        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.execute(() -> sleep(2000, "读取数据库"));
        executor.execute(() -> sleep(3000, "计算数据"));
        executor.execute(() -> sleep(2000, "写入文件"));

        executor.shutdown();
        while (!executor.isTerminated()) { Thread.sleep(100); }

        System.out.println("总耗时: " + (System.currentTimeMillis() - start) + " ms");
    }

    static void sleep(int millis, String task) {
        try { 
            System.out.println(task + " 开始...");
            Thread.sleep(millis); 
            System.out.println(task + " 结束");
        } catch (InterruptedException e) {}
    }
}

执行结果(并行执行,可能不同)

复制代码
读取数据库 开始...
计算数据 开始...
写入文件 开始...
读取数据库 结束
写入文件 结束
计算数据 结束
总耗时: 3000 ms

并行执行,耗时大幅减少!

🚀 比串行执行快了 7s → 3s,大幅优化了执行时间。


2. 并行 vs. 串行:如何选择?

对比项 串行(Serial) 并行(Parallel)
执行方式 任务逐个执行 任务同时执行
CPU 利用率
吞吐量
适用场景 任务依赖强,逻辑顺序不可分 任务独立,计算密集型
资源消耗 低(一个线程) 高(多线程)
调试难度 简单 复杂(需考虑同步、锁)

🚀 适合使用并行的场景

计算密集型任务 (如大数据计算、AI 训练)

I/O 密集型任务 (如多个 API 调用、批量文件处理)

任务独立,可拆分成多个子任务

🚨 不适合并行的场景

任务之间强依赖 (如步骤 A 结果决定 B 的输入)

线程切换开销大,反而影响性能(如小任务并行导致频繁上下文切换)


3. Java 并行实战

(1)使用 ForkJoinPool 并行计算

ForkJoinPool 是 Java 7 引入的 并行计算框架 ,适用于大规模递归任务,如 并行数组求和

java 复制代码
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

class ParallelSum extends RecursiveTask<Long> {
    private static final int THRESHOLD = 10000;
    private long[] array;
    private int start, end;

    public ParallelSum(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    protected Long compute() {
        if ((end - start) <= THRESHOLD) { 
            long sum = 0;
            for (int i = start; i < end; i++) sum += array[i];
            return sum;
        } else {
            int mid = (start + end) / 2;
            ParallelSum leftTask = new ParallelSum(array, start, mid);
            ParallelSum rightTask = new ParallelSum(array, mid, end);
            leftTask.fork();  // 异步执行左半部分
            return rightTask.compute() + leftTask.join();
        }
    }
}

public class ForkJoinExample {
    public static void main(String[] args) {
        long[] array = new long[1000000];
        for (int i = 0; i < array.length; i++) array[i] = i;

        ForkJoinPool pool = new ForkJoinPool();
        long sum = pool.invoke(new ParallelSum(array, 0, array.length));

        System.out.println("总和: " + sum);
    }
}

适用于大规模数据计算,充分利用 CPU 并行能力!


(2)使用 parallelStream() 并行处理集合

java 复制代码
import java.util.List;
import java.util.stream.IntStream;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = IntStream.rangeClosed(1, 1000000).boxed().toList();

        long start = System.currentTimeMillis();
        int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
        System.out.println("总和: " + sum + ", 耗时: " + (System.currentTimeMillis() - start) + " ms");
    }
}

比传统 for 循环更快!


4. 总结

串行适用于任务依赖强、执行顺序严格的场景

并行适用于 CPU 密集、I/O 密集、可拆分任务

合理选择 ForkJoinPoolparallelStream() 提升效率!

并行计算是高性能开发的关键,希望本文能帮助你更高效地编写 Java 并行程序!🚀🚀🚀

相关推荐
年老体衰按不动键盘5 分钟前
快速部署和启动Vue3项目
java·javascript·vue
咖啡啡不加糖10 分钟前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
liuyang-neu14 分钟前
java内存模型JMM
java·开发语言
UFIT34 分钟前
NoSQL之redis哨兵
java·前端·算法
刘 大 望38 分钟前
数据库-联合查询(内连接外连接),子查询,合并查询
java·数据库·sql·mysql
怀旧,44 分钟前
【数据结构】6. 时间与空间复杂度
java·数据结构·算法
大春儿的试验田1 小时前
Parameter ‘XXX‘ not found. Available parameters are [list, param1]
java
程序员JerrySUN2 小时前
[特殊字符] 深入理解 Linux 内核进程管理:架构、核心函数与调度机制
java·linux·架构
2302_809798322 小时前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
网安INF2 小时前
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
java·web安全·网络安全·flink·漏洞