• 作者:老汪软件技巧
  • 发表时间:2024-12-16 00:16
  • 浏览量:

你好,我是猿java。

在计算机科学中,“并发”和“并行性”是两个经常被混淆但实际上具有不同含义的概念。这篇文章,我们将深入探讨这两个概念,并通过Java代码演示它们的实现。

关于并行和并发, 先看一张很形象的 gif图片(图片来自网络):

1. 并发1.1 定义

**并发性(Concurrency)**是指系统能够处理多个任务,但不一定是同时执行。关键在于任务的管理,使得多个任务在时间上交错进行,以提高资源利用率和响应能力。

如下图,在一个 CPU上,交替执行多个task:

concurrency-parallelism-2.png

1.2 特点1.3 实际应用示例1.4 Java实现示例

以下是一个简单的Java并发示例,模拟多个任务交替执行。

public class ConcurrencyExample {
    public static void main(String[] args) {
        Runnable task1 = () -> {
            for(int i=1; i<=5; i++) {
                System.out.println("Task1 - Count: " + i);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            }
        };
        Runnable task2 = () -> {
            for(int i=1; i<=5; i++) {
                System.out.println("Task2 - Count: " + i);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            }
        };
        Thread thread1 = new Thread(task1);
        Thread thread2 = new Thread(task2);
        thread1.start();
        thread2.start();
    }
}

输出示例(任务交错执行):

Task1 - Count: 1
Task2 - Count: 1
Task1 - Count: 2
Task2 - Count: 2
...

在这个例子中,两个任务在同一个处理器上交替执行,实现了并发性。

2. 并行性2.1 定义

**并行性(Parallelism)**是指利用多核或多处理器系统同时执行多个任务或任务的多个部分,以加快总体处理速度。

如下图,多个CPU,每个CPU上分别执行一个 task:

concurrency-parallelism-3.png

2.2 特点2.3 实际应用示例机器学习训练:训练深度学习模型涉及将数据集划分为较小的批次。每个批次都跨多个 GPU 或 CPU 内核同时处理,从而显著加快了训练过程。视频渲染:视频帧是独立渲染的,因此可以同时处理多个帧。 例如,当使用多个内核并行处理不同的帧时,渲染 3D 动画的速度会快得多。网络爬虫:像 Googlebot 这样的网络爬虫将 URL 列表分解成更小的块,并并行处理它们。这允许爬虫同时从多个网站获取数据,从而减少收集信息的时间。数据处理:Apache Spark 等大数据框架利用并行性来处理海量数据集。分析来自数百万用户的日志等任务分布在集群中,从而实现同步处理和更快的洞察。科学模拟:天气建模或分子相互作用等模拟需要大量的计算。这些计算在多个内核之间分配,允许同时执行并更快地获得结果。2.4 Java实现示例

以下是一个使用Java并行流实现并行计算的示例,计算1到1000000的平方和。

import java.util.stream.LongStream;
public class ParallelismExample {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(1, 1_000_000)
                             .parallel()
                             .map(x -> x * x)
                             .sum();
        long endTime = System.currentTimeMillis();
        System.out.println("Sum: " + sum);
        System.out.println("Time taken: " + (endTime - startTime) + " ms");
    }
}

输出示例:

Sum: 333333833333500000
Time taken: 50 ms

在多核处理器上,.parallel()方法使得流操作并行执行,从而加快计算速度。

_并发并行区别_并发和并行有何区别和联系

3. 并发与并行性的对比3.1 目标3.2 示例对比3.3 性能考虑3.4 资源利用4. 通过并发和并行实现的Java框架

在 Java中,提供了丰富的工具和库来实现并发和并行操作,下面分别举一个例子来展示并发和并行。

4.1 线程和Executor框架

线程是实现并发的基本单元,Java通过Thread类和Runnable接口提供了对线程的支持。但直接使用Thread可能导致资源管理困难,因此Java引入了Executor框架,简化线程管理。

示例:使用ExecutorService实现并发

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorConcurrencyExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Runnable task1 = () -> {
            for(int i=1; i<=5; i++) {
                System.out.println("Task1 - Count: " + i);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            }
        };
        Runnable task2 = () -> {
            for(int i=1; i<=5; i++) {
                System.out.println("Task2 - Count: " + i);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            }
        };
        executor.submit(task1);
        executor.submit(task2);
        executor.shutdown();
    }
}

输出示例:

Task1 - Count: 1
Task2 - Count: 1
Task1 - Count: 2
Task2 - Count: 2
...

4.2 并行流(Parallel Streams)

Java 8引入了Streams API,它支持顺序和并行操作,极大简化了并行处理的编程复杂度。通过调用.parallel(),可以轻松将流操作并行化。

示例:并行处理列表

import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
    public static void main(String[] args) {
        List numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        numbers.parallelStream()
               .map(n -> {
                   System.out.println("Processing " + n + " in " + Thread.currentThread().getName());
                   return n * n;
               })
               .forEach(result -> System.out.println("Result: " + result));
    }
}

输出示例(线程顺序可能不同):

Processing 2 in ForkJoinPool.commonPool-worker-1
Result: 4
Processing 1 in ForkJoinPool.commonPool-worker-3
Result: 1
...

5. 实践建议选择合适的并发工具:对于简单的线程管理,可以使用ExecutorService;对于复杂的任务调度,考虑使用ForkJoinPool。避免共享可变状态:共享状态可能导致竞态条件(Race Conditions),使用线程安全的数据结构或同步机制。理解任务的性质:I/O密集型任务适合并发处理,CPU密集型任务适合并行处理。合理划分任务:避免过度划分导致线程切换开销过大,或任务粒度过粗导致资源浪费。使用高层次抽象:如Java 8的CompletableFuture,简化异步编程模型。6. 总结

本文从多个维度对比了并发和并行,虽然在处理多任务方面它们有共同之处,但它们的目标和实现方式不同。并发性侧重于任务的管理和调度,以提高系统的响应能力和资源利用率;而并行性则侧重于通过同时执行多个任务或任务的多个部分,以提升处理速度和吞吐量。

7. 学习交流