本文主要對JAVA并發(Concurrent)相關的概念進行說明。
1.進程(Process)與線程(Thread)
- 進程是系統資源分配的最小單元。線程是CPU調度的最小單元。
- 一個 進程至少包含一個線程,可以包含多個線程。這些線程共享這個進程的資源。
- 每個線程都擁有獨立的運行棧和程序計數器,線程切換開銷小。
- 多進程指的是操作系統同時運行多個程序,如當前操作系統中同時運行著QQ、IE、微信等程序。
- 多線程指的是同一進程中同時運行多個線程,如迅雷運行時,可以開啟多個線程,同時進行多個文件的下載。
2.并行(Parallel)、并發(Concurrent)與多線程(Multithreading)
- 并行是指多個任務在同一時刻進行。并發是指多個任務在同一時間段內發生。
- 并行是物理上的同時發生,而并發是邏輯上的同時發生。
- 體現在程序上: 并行是指多個CPU內核在同一時刻,同時運行多個任務。并發是指單個CPU內,通過CPU調度算法,讓用戶感覺在同時運行多個任務。
- 更形象的說法: 并行是指兩隊人員同時使用兩臺咖啡機。并發是指兩隊人員交替使用同一臺咖啡機。—-Erlang 之父 Joe Armstrong。
- 多線程即多個線程,一般只多個同時在運行的單線程。
- 如果是在單核CPU上,多線程肯定是并發運行的。如果是在多核CPU上,這些多線程也可能是并行運行。
3.線程安全
線程安全,指的是在并發的情況之下,線程的調度順序不影響運行結果。
如不加鎖控制的轉賬操作,在單線程運行中是安全的,但是在多線程環境中,肯定是不安全的。這里只給出示例,具體邏輯就沒不解釋了。
void transferMoney(User from, User to, float amount){
to.setMoney(to.getBalance() + amount);
from.setMoney(from.getBalance() - amount);
}
4.死鎖
死鎖是指兩個或更多線程阻塞著等待其它處于死鎖狀態的線程所持有的鎖。
例如,
- 如果線程1鎖住了A,然后嘗試對B進行加鎖,同時線程2已經鎖住了B,接著嘗試對A進行加鎖,這時死鎖就發生了。
- 線程1永遠得不到B,線程2也永遠得不到A,并且它們永遠也不會知道發生了這樣的事情。
- 為了得到彼此的對象(A和B),它們將永遠阻塞下去。這種情況就是一個死鎖。
5.并發優點
5.1.資源利用率高
假定場景:需要從本地讀取和處理兩個文件,讀取一個文件需要5秒,處理一個文件需要2秒。單線程:
5秒讀取文件A
2秒處理文件A
5秒讀取文件B
2秒處理文件B
---------------------
總共需要14秒
多線程:
5秒讀取文件A
5秒讀取文件B + 2秒處理文件A
2秒處理文件B
---------------------
總共需要12秒
總結:
- 在等待磁盤讀取文件的時候,CPU大部分時間是空閑的。
- 利用多線程編程,可以在磁盤讀取文件的CPU空閑時間內做一些其他的事情。
- 所以說, 使用多線程資源利用率更高。
5.2.程序響應更快
一個桌面應用程序存在多個按鈕。點擊這些按鈕,可以分別進行一些耗時的工作。
單線程:
每次點擊一個按鈕,都會進行一項耗時的工作。必須等待此項工作完成之后,才能繼續監聽用戶操作。這時,用戶才能點擊其他按鈕,進行其他的工作。這樣的應用程序,對用戶而言,響應十分慢,體驗度很差。
多線程:
每次點擊一個按鈕,都會啟動一個子線程去進行這項耗時的工作,主線程繼續監聽用戶操作。這種情況下,用戶可以快速的分別點擊需要處理的按鈕。這樣的應用程序,對用戶而言,響應很快,體驗度很好。
6.并發缺點
6.1.設計開發更復雜
線程之間的交互往往非常復雜。 不正確的線程同步產生的錯誤非常難以發現,并且難以重現、難以修復。
6.2.增加額外資源消耗
多線程開發會產生額外的資源消耗,主要來源于三個方面:
- 線程本身需要消耗一些資源進行本地堆棧的維持與管理。
- 線程之間切換會導致的上下文切換(Context Switch)開銷。
- 多線程的管理對CPU來說又是一筆開銷。
所以,由于多線程會增加額外的資源消耗,對多線程程序而言,線程并不是越多就會越快,過多的線程返回會導致程序變慢。