并發(fā)和并行,如何區(qū)分?如何使用?
在計(jì)算機(jī)科學(xué)中,“并發(fā)”和“并行性”是兩個(gè)經(jīng)常被混淆但實(shí)際上具有不同含義的概念。這篇文章,我們將深入探討這兩個(gè)概念,并通過(guò)Java代碼演示它們的實(shí)現(xiàn)。
關(guān)于并行和并發(fā), 先看一張很形象的 gif圖片(圖片來(lái)自網(wǎng)絡(luò)):
接著,我們對(duì)照這上面的gif圖來(lái)詳細(xì)地分析兩者。
1. 并發(fā)
(1) 定義
并發(fā)性(Concurrency)是指系統(tǒng)能夠處理多個(gè)任務(wù),但不一定是同時(shí)執(zhí)行。關(guān)鍵在于任務(wù)的管理,使得多個(gè)任務(wù)在時(shí)間上交錯(cuò)進(jìn)行,以提高資源利用率和響應(yīng)能力。
如下圖,在一個(gè) CPU上,交替執(zhí)行多個(gè)task:
(2) 特點(diǎn)
- 任務(wù)切換:在單核或多核系統(tǒng)上,通過(guò)快速切換任務(wù),讓用戶(hù)感覺(jué)任務(wù)是同時(shí)進(jìn)行的。
- 資源共享:多個(gè)任務(wù)共享系統(tǒng)資源,如CPU、內(nèi)存等。
- 異步處理:任務(wù)可以在等待某些操作完成(如I/O)時(shí),切換到其他任務(wù)。
(3) 實(shí)際應(yīng)用示例
- 用戶(hù)界面:在圖形用戶(hù)界面(GUI)中,主線(xiàn)程負(fù)責(zé)響應(yīng)用戶(hù)輸入,而后臺(tái)線(xiàn)程處理耗時(shí)操作,使界面保持響應(yīng)。
- 服務(wù)器處理:Web服務(wù)器同時(shí)處理多個(gè)客戶(hù)端請(qǐng)求,通過(guò)線(xiàn)程池或異步IO管理并發(fā)連接。
(4) Java實(shí)現(xiàn)示例
以下是一個(gè)簡(jiǎn)單的Java并發(fā)示例,模擬多個(gè)任務(wù)交替執(zhí)行。
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();
}
}
輸出示例(任務(wù)交錯(cuò)執(zhí)行):
Task1 - Count: 1
Task2 - Count: 1
Task1 - Count: 2
Task2 - Count: 2
...
在這個(gè)例子中,兩個(gè)任務(wù)在同一個(gè)處理器上交替執(zhí)行,實(shí)現(xiàn)了并發(fā)性。
2. 并行性
(1) 定義
并行性(Parallelism)是指利用多核或多處理器系統(tǒng)同時(shí)執(zhí)行多個(gè)任務(wù)或任務(wù)的多個(gè)部分,以加快總體處理速度。
如下圖,多個(gè)CPU,每個(gè)CPU上分別執(zhí)行一個(gè) task:
(2) 特點(diǎn)
- 真實(shí)的同時(shí)執(zhí)行:在多核處理器上,多個(gè)任務(wù)可以在不同的核心上同時(shí)運(yùn)行。
- 任務(wù)分解:大的任務(wù)可以分解為多個(gè)子任務(wù),并行處理后合并結(jié)果。
- 性能提升:通過(guò)并行執(zhí)行,能夠顯著縮短處理時(shí)間,尤其適合計(jì)算密集型任務(wù)。
(3) 實(shí)際應(yīng)用示例
- 科學(xué)計(jì)算:數(shù)值模擬、天氣預(yù)報(bào)等需要處理大量數(shù)據(jù)的應(yīng)用程序。
- 大數(shù)據(jù)處理:Hadoop、Spark等框架通過(guò)并行計(jì)算提高數(shù)據(jù)處理速度。
(4) Java實(shí)現(xiàn)示例
以下是一個(gè)使用Java并行流實(shí)現(xiàn)并行計(jì)算的示例,計(jì)算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()方法使得流操作并行執(zhí)行,從而加快計(jì)算速度。
3. 并發(fā)與并行性的對(duì)比
(1) 目標(biāo)
- 并發(fā)性:使多個(gè)任務(wù)有條不紊地進(jìn)行,增強(qiáng)系統(tǒng)響應(yīng)能力和資源利用率。
- 并行性:通過(guò)同時(shí)執(zhí)行多個(gè)任務(wù),提升總體處理速度和吞吐量。
(2) 示例對(duì)比
- 并發(fā):?jiǎn)魏颂幚砥魃?,通過(guò)時(shí)間片輪轉(zhuǎn)執(zhí)行多個(gè)任務(wù),使用戶(hù)感覺(jué)多個(gè)任務(wù)同時(shí)進(jìn)行。
- 并行:多核處理器上,多個(gè)任務(wù)或任務(wù)的部分在不同核心上同時(shí)執(zhí)行。
(3) 性能考慮
- 并發(fā)性適用于I/O密集型應(yīng)用,通過(guò)管理任務(wù)等待時(shí)間提高系統(tǒng)效率。
- 并行性適用于CPU密集型應(yīng)用,通過(guò)利用多核資源加快計(jì)算速度。
(4) 資源利用
- 并發(fā)更關(guān)注任務(wù)的調(diào)度和資源的共享。
- 并行更關(guān)注如何劃分任務(wù)以充分利用多核資源。
4. 通過(guò)并發(fā)和并行實(shí)現(xiàn)的Java框架
在 Java中,提供了豐富的工具和庫(kù)來(lái)實(shí)現(xiàn)并發(fā)和并行操作,下面分別舉一個(gè)例子來(lái)展示并發(fā)和并行的實(shí)際使用。
(1) 線(xiàn)程和Executor框架
線(xiàn)程是實(shí)現(xiàn)并發(fā)的基本單元,Java通過(guò)Thread類(lèi)和Runnable接口提供了對(duì)線(xiàn)程的支持。但直接使用Thread可能導(dǎo)致資源管理困難,因此Java引入了Executor框架,簡(jiǎn)化線(xiàn)程管理。
示例:使用ExecutorService實(shí)現(xiàn)并發(fā)
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,它支持順序和并行操作,極大簡(jiǎn)化了并行處理的編程復(fù)雜度。通過(guò)調(diào)用.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));
}
}
輸出示例(線(xiàn)程順序可能不同):
Processing 2 in ForkJoinPool.commonPool-worker-1
Result: 4
Processing 1 in ForkJoinPool.commonPool-worker-3
Result: 1
...
5. 實(shí)踐建議
- 選擇合適的并發(fā)工具:對(duì)于簡(jiǎn)單的線(xiàn)程管理,可以使用ExecutorService;對(duì)于復(fù)雜的任務(wù)調(diào)度,考慮使用ForkJoinPool。
- 避免共享可變狀態(tài):共享狀態(tài)可能導(dǎo)致競(jìng)態(tài)條件(Race Conditions),使用線(xiàn)程安全的數(shù)據(jù)結(jié)構(gòu)或同步機(jī)制。
- 理解任務(wù)的性質(zhì):I/O密集型任務(wù)適合并發(fā)處理,CPU密集型任務(wù)適合并行處理。
- 合理劃分任務(wù):避免過(guò)度劃分導(dǎo)致線(xiàn)程切換開(kāi)銷(xiāo)過(guò)大,或任務(wù)粒度過(guò)粗導(dǎo)致資源浪費(fèi)。
- 使用高層次抽象:如Java 8的CompletableFuture,簡(jiǎn)化異步編程模型。
6. 總結(jié)
本文,我們從多個(gè)維度對(duì)比了并發(fā)和并行,雖然在處理多任務(wù)方面它們有共同之處,但它們的目標(biāo)和實(shí)現(xiàn)方式不同。并發(fā)性側(cè)重于任務(wù)的管理和調(diào)度,以提高系統(tǒng)的響應(yīng)能力和資源利用率;而并行性則側(cè)重于通過(guò)同時(shí)執(zhí)行多個(gè)任務(wù)或任務(wù)的多個(gè)部分,以提升處理速度和吞吐量。
- 并發(fā)性關(guān)注的是如何結(jié)構(gòu)化程序以處理多個(gè)任務(wù)的進(jìn)展,不一定同時(shí)執(zhí)行。
- 并行性關(guān)注的是如何同時(shí)執(zhí)行多個(gè)任務(wù),以加快總體的處理速度。