問題:串行(Sequential)、并行(parallel)、并發(Concurrent)和分布式(distributed)的區別是什么?
1. 概念
假設有AB兩個任務,則串行、并行、并發的區別如圖1所示。
串行
A和B兩個任務運行在一個CPU線程上,在A任務執行完之前不可以執行B。即,在整個程序的運行過程中,僅存在一個運行上下文,即一個調用棧一個堆。程序會按順序執行每個指令。
并行
并行性指兩個或兩個以上事件或活動在同一時刻發生。在多道程序環境下,并行性使多個程序同一時刻可在不同CPU上同時執行。比如,A和B兩個任務可以同時運行在不同的CPU線程上,效率較高,但受限于CPU線程數,如果任務數量超過了CPU線程數,那么每個線程上的任務仍然是順序執行的。
并發
并發指多個線程在宏觀(相對于較長的時間區間而言)上表現為同時執行,而實際上是輪流穿插著執行,并發的實質是一個物理CPU在若干道程序之間多路復用,其目的是提高有限物理資源的運行效率。 并發與并行串行并不是互斥的概念,如果是在一個CPU線程上啟用并發,那么自然就還是串行的,而如果在多個線程上啟用并發,那么程序的執行就可以是既并發又并行的。
而分布式和并行的區別如下:
分布式
分布式在并行處理的基礎上,強調任務正在執行的物理設備,如處理器、內存等等硬件,在物理上是分開的。而并行計算是指在一臺計算機上的計算,在物理上不分開。
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兩個任務。程序運行結果如下:采用并發的方法設計的程序如下:
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在任務切換過程中需要消耗一定時間。程序運行結果如下:采用并行的方法設計的程序如下:
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任務的最長耗時,程序運行結果如下:模式 | CPU線程數 | 總耗時 |
---|---|---|
串行 | 1 | 2736毫秒 |
并發 | 1 | 2933毫秒 |
并行 | 2 | 2277毫秒 |
3.總結
由上表可知并行的總耗時是最小的,效率最高(如果AB兩個任務耗時更接近,則并行計算的效率將更高)。但由于并行計算受限于CPU線程數,當計算量超出單臺計算機的計算能力時,人們就開始考慮使用多臺計算機同時處理一個任務,分布式計算應用而生。分布式計算將任務分解成許多小的部分,分配給多臺計算機進行處理,從而整體上節約了計算時間。Hadoop的MapReduce就是一種分布式計算框架,我們之后會對MapReduce進行詳細的探討。