在计算机科学中,“并发”和“并行性”是两个经常被混淆但实际上具有不同含义的概念。这篇文章,我们将深入探讨这两个概念,并通过Java代码演示它们的实现。
关于并行和并发, 先看一张很形象的 gif图片(图片来自网络):
接着,我们对照这上面的gif图来详细地分析两者。
1. 并发
(1) 定义
并发性(Concurrency)是指系统能够处理多个任务,但不一定是同时执行。关键在于任务的管理,使得多个任务在时间上交错进行,以提高资源利用率和响应能力。
如下图,在一个 CPU上,交替执行多个task:
(2) 特点
- 任务切换:在单核或多核系统上,通过快速切换任务,让用户感觉任务是同时进行的。
- 资源共享:多个任务共享系统资源,如CPU、内存等。
- 异步处理:任务可以在等待某些操作完成(如I/O)时,切换到其他任务。
(3) 实际应用示例
- 用户界面:在图形用户界面(GUI)中,主线程负责响应用户输入,而后台线程处理耗时操作,使界面保持响应。
- 服务器处理:Web服务器同时处理多个客户端请求,通过线程池或异步IO管理并发连接。
(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. 并行性
(1) 定义
并行性(Parallelism)是指利用多核或多处理器系统同时执行多个任务或任务的多个部分,以加快总体处理速度。
如下图,多个CPU,每个CPU上分别执行一个 task:
(2) 特点
- 真实的同时执行:在多核处理器上,多个任务可以在不同的核心上同时运行。
- 任务分解:大的任务可以分解为多个子任务,并行处理后合并结果。
- 性能提升:通过并行执行,能够显著缩短处理时间,尤其适合计算密集型任务。
(3) 实际应用示例
- 科学计算:数值模拟、天气预报等需要处理大量数据的应用程序。
- 大数据处理:Hadoop、Spark等框架通过并行计算提高数据处理速度。
(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. 并发与并行性的对比
(1) 目标
- 并发性:使多个任务有条不紊地进行,增强系统响应能力和资源利用率。
- 并行性:通过同时执行多个任务,提升总体处理速度和吞吐量。
(2) 示例对比
- 并发:单核处理器上,通过时间片轮转执行多个任务,使用户感觉多个任务同时进行。
- 并行:多核处理器上,多个任务或任务的部分在不同核心上同时执行。
(3) 性能考虑
- 并发性适用于I/O密集型应用,通过管理任务等待时间提高系统效率。
- 并行性适用于CPU密集型应用,通过利用多核资源加快计算速度。
(4) 资源利用
- 并发更关注任务的调度和资源的共享。
- 并行更关注如何划分任务以充分利用多核资源。
4. 通过并发和并行实现的Java框架
在 Java中,提供了丰富的工具和库来实现并发和并行操作,下面分别举一个例子来展示并发和并行的实际使用。
(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
...
(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<Integer> 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. 总结
本文,我们从多个维度对比了并发和并行,虽然在处理多任务方面它们有共同之处,但它们的目标和实现方式不同。并发性侧重于任务的管理和调度,以提高系统的响应能力和资源利用率;而并行性则侧重于通过同时执行多个任务或任务的多个部分,以提升处理速度和吞吐量。
- 并发性关注的是如何结构化程序以处理多个任务的进展,不一定同时执行。
- 并行性关注的是如何同时执行多个任务,以加快总体的处理速度。