问题:串行(Sequential)、并行(parallel)、并发(Concurrent)和分布式(distributed)的区别是什么?
1. 概念
假设有AB两个任务,则串行、并行、并发的区别如图1所示。
串行
A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。
并行
并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。比如,A和B两个任务可以同时运行在不同的CPU线程上,效率较高,但受限于CPU线程数,如果任务数量超过了CPU线程数,那么每个线程上的任务仍然是顺序执行的。
并发
并发指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,而实际上是轮流穿插着执行,并发的实质是一个物理CPU在若干道程序之间多路复用,其目的是提高有限物理资源的运行效率。 并发与并行串行并不是互斥的概念,如果是在一个CPU线程上启用并发,那么自然就还是串行的,而如果在多个线程上启用并发,那么程序的执行就可以是既并发又并行的。
![](https://img.haomeiwen.com/i12652505/16c4553be23dfc0f.png)
而分布式和并行的区别如下:
分布式
分布式在并行处理的基础上,强调任务正在执行的物理设备,如处理器、内存等等硬件,在物理上是分开的。而并行计算是指在一台计算机上的计算,在物理上不分开。
2. 例子
假设有A,B两个任务,任务A需要计算1-100000之间所有质数的和,任务B需要计算100001-200000之间所有质数的和。
则采用串行的方法设计的程序如下:
public class Main {
//判断是否为质数
private static boolean isPrime(int n) {
if(n < 2) return false;
if(n == 2) return true;
if(n%2==0) return false;
for(int i = 3; i < n; i += 2)
if(n%i == 0) return false;
return true;
}
//串行计算
private static void serial() {
long time1 = System.currentTimeMillis(), time2,time3;
long count = 0;
for(int i=1;i<=100000;++i){
if(isPrime(i)) count+=i;
}
time2=System.currentTimeMillis();
System.out.println("1-100000之间质数和为"+count+" 耗时:"+(time2- time1) + "毫秒");
count = 0;
for(int i=100001;i<=200000;++i){
if(isPrime(i))
count+=i;
}
time3 = System.currentTimeMillis();
System.out.println("100001-200000之间质数和为"+count+" 耗时:"+(time3 - time2) + "毫秒");
System.out.println("总耗时:"+ (time3 - time1) + "毫秒");
}
//主函数
public static void main(String[] args) {
serial();
}
}
在串行计算的程序中,只有一个CPU线程,且该线程按顺序执行AB两个任务。程序运行结果如下:
![](https://img.haomeiwen.com/i12652505/dff63fea7bf47954.png)
采用并发的方法设计的程序如下:
public class Main{
private static boolean isPrime(int n) {
if(n < 2) return false;
if(n == 2) return true;
if(n%2==0) return false;
for(int i = 3; i < n; i += 2)
if(n%i == 0) return false;
return true;
}
public static void main(String[] args) {
serialConcurrency();
}
private static void serialConcurrency() {
long time = System.currentTimeMillis();
//任务切换标识,1代表A任务,2代表B任务
int task = 1;
//计数器
long count1 = 0, count2 = 0;
int i=1,j=100001;
while (true)
{
if(task == 1 && i++<=100000) {
if(isPrime(i)) count1+=i;
task = 2;
}
else if(task == 2 && j++<=200000) {
if(isPrime(j)) count2+=j;
task = 1;
}
else{
break;
}
}
System.out.println("1-100000之间质数和为"+count1);
System.out.println("100001-200000之间质数和为"+count2);
System.out.println("总耗时:"+(System.currentTimeMillis() - time) + "毫秒");
}
}
在并发计算的程序中,同样只有一个CPU线程,但是该线程会在AB两个任务之间进行切换,可以发现,并发计算的总耗时反而大于串行计算,这是因为CPU在任务切换过程中需要消耗一定时间。程序运行结果如下:
![](https://img.haomeiwen.com/i12652505/b40101decd0794ee.png)
采用并行的方法设计的程序如下:
public class Main {
public static boolean isPrime(int n) {
if(n < 2) return false;
if(n == 2) return true;
if(n%2==0) return false;
for(int i = 3; i < n; i += 2)
if(n%i == 0) return false;
return true;
}
public static void main(String[] args) throws InterruptedException {
long time1 = System.currentTimeMillis(),time2;
Task task1 = new Task(1,100000);
Task task2 = new Task(100001,200000);
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()){
Thread.sleep(1);
}
time2 = System.currentTimeMillis();
System.out.println("总耗时:"+(time2 - time1)+"毫秒");
}
}
class Task implements Runnable{
private int start;
private int end;
Task(int start, int end) {
this.start = start;
this.end = end;
}
public void run() {
long time = System.currentTimeMillis();
long count = 0;
for(int i=start;i<=end;++i){
if(Main.isPrime(i)) count+=i;
}
System.out.println(String.format("%d-%d之间质数和为%d,耗时:%d毫秒",start,end,count,(System.currentTimeMillis()- time)));
}
}
在并行计算的程序中,AB任务各占用一个CPU线程,AB任务同时执行,总共耗费的时间约等于AB任务的最长耗时,程序运行结果如下:
![](https://img.haomeiwen.com/i12652505/8663e7e14b60a692.png)
模式 | CPU线程数 | 总耗时 |
---|---|---|
串行 | 1 | 2736毫秒 |
并发 | 1 | 2933毫秒 |
并行 | 2 | 2277毫秒 |
3.总结
由上表可知并行的总耗时是最小的,效率最高(如果AB两个任务耗时更接近,则并行计算的效率将更高)。但由于并行计算受限于CPU线程数,当计算量超出单台计算机的计算能力时,人们就开始考虑使用多台计算机同时处理一个任务,分布式计算应用而生。分布式计算将任务分解成许多小的部分,分配给多台计算机进行处理,从而整体上节约了计算时间。Hadoop的MapReduce就是一种分布式计算框架,我们之后会对MapReduce进行详细的探讨。
网友评论