並發(1)簡介
一、什麽是進程、線程
在早期的計算機中,從計算機啟動到關閉只執行一個程序,這個程序能夠訪問計算機內的所有資源。在計算機程序啟動執行的時候你無法做任何別的事,這種裸機環境中是對資源極大的浪費。
那麽這個時候,操作系統就應運而生了。在操作系統中,你可以運行多個應用程序,每個程序運行在獨立的進程當中,操作系統通過調度進程,給他們分配時間片並快速切換執行來達到“同時執行”目的。
你可以把進程簡單理解為計算機程序的運行,是操作系統基本的調度單位。
那什麽是線程呢?我們思考一個問題,計算機為了同時運行多個程序從而產生了進程,那麽每個程序是否能夠執行多個任務而不是從頭到尾只做一件事呢?答案是可以的,這就是線程的意義所在。在大多數現代操作系統中,更多的是以線程為最基本的調度單位,而不是線程。一個進程中包含多個線程,每個線程彼此獨立,共享進程當中的時間片、內存空間
二、優缺點
優點:
1)發揮多處理器的強大能力
我們都聽過多核處理器,那麽如果我們的程序只有一個線程,那它最多同時只能利用到一個處理器。如果是雙核處理器,那麽就有一半的資源閑置。如果有100核的處理器,那麽你的程序就浪費了99%的處理器資源。多線程可以讓你充分地利用處理器資源,從而使程序獲得高性能並發的能力。
2)提高用戶界面的響應速度
傳統的GUI應用程序是單線程的,你可以預想到,如果一個操作卡住了整個應用程序就卡死在那裏。或者你想要下載一個資源,卻發現你必須等待在那裏直勾勾地看著下載進度慢慢增加而不能挑選更多令自己滿意的資源。那麽多線程可以幫你解決這個問題,你可以在下載的時候做別的事,挑選更多的資源,它下載完畢以後你再去關註下載好的資源。
3)異步事件簡化
在單線程應用中,為了避免一個請求阻塞導致整個程序停頓,通常會采用非阻塞IO的方式,但是這種方式的復雜性遠遠高於同步的IO,很容易產生錯誤。那麽,如果每個請求都在一個獨立的線程當中呢?一個請求的阻塞將和其它請求互不影響,那也就不會發生上面提到的一個請求阻塞整個程序的現象。
4)建模的簡單性
如果我們要構建一個很大的模型,那麽就很考驗你的構建能力。你必須有著豐富的經驗,以及盡最大能力保證每個地方都不出錯。多線程在這方面提供了幫助,例如:你可以將一個復雜的工作流給分解,每一個組分配在一個單獨的線程裏運行,並在特定的位置進行交互(其實可以理解為“異步解耦”)。你可能會顧慮到將一個工作流拆分,是否會帶來很高的編程復雜性?確實,如果你必須從零開始寫所有東西這將會對你的編程能力有著很大的考驗。但是我們可以通過一些現有的成熟框架來實現上面的目標,例如:Servlet、RMI等。框架負責大多數線程的細節問題,同步調用你的業務代碼
缺點:
1)安全性
在並發編程中線程安全是不可破壞的,它對於多線程應用程序非常重要。
我們看一段代碼片段
public class Test{ private int i = 1; public void incrI(){ ++i; } }
在單線程情況下,以上的代碼不會有什麽問題。但是在多線程的情況下你可能會遇到這樣的情況:
1、線程1取到值i = 1,線程2取到i = 1;
2、線程1將i + 1 = 2,線程2將i + 1 = 2;
3、線程1賦值 i = 2,線程2賦值i= 2。
我們看到,最終的結果是2,而不是我們預期的結果1 + 1 = 2; 2 + 1 = 3;
所以,多線程在交替執行的時候,由於執行時間上的問題會帶來變量操作的安全性問題(結果與預期不符)。
註意:自增自減操作並不是原子操作,例如自增包含了3個動作取值、加法計算、賦值。
2)性能問題
雖然多線程能夠充分利用CPU,從一定程度上你也可以理解為提高程序的性能。但區別於串行程序,多線程將會頻繁地出現上下文切換,CPU更多地花在線程調度上,以及針對共享數據你需要采用同步機制等,以上這些問題將會給程序帶來額外開銷。所以多線程程序還需要做的一件事是如何分析和減少這些額外的開銷。
3)活躍性問題
什麽是活躍性?活躍性意味著你的程序總是能夠及時地執行。活躍性問題就是說你的程序無法正確的繼續執行,例如:死鎖、饑餓、活鎖。與大多數並發問題一樣,如果發生了活躍性問題,通常是難以解決的,甚至於分析都無從下手,因為他們執行的時間是沒有規律的你難以重現發生的問題。
並發(1)簡介