一切都要從這個世界的並行性開始說,事物的發展老是並行進行的,汽車在奔馳的同時,自行車也在行駛;別人正在唱歌,你可能正在吃飯;等等。這些都是並行的例子,就單個事物來看,它發展的動做通常是順序進行,而多個事物之間通常誰也不會妨礙誰,除非它們的動做要做用在同一個對象上:汽車過十字路口的綠燈時,另外一個方向的汽車就不能行駛;若是你在食堂裏舉辦歌舞晚會,別人就不能在這兒吃飯。
計算機軟件的基本模型是順序執行的,然而現代計算機在此基礎上發展出了併發概念。什麼是併發?什麼是並行?它們之間的區別是什麼?併發與並行是兩個既類似而又不相同的概念:併發性,又稱共行性,是指能處理多個同時性活動的能力;並行是指同時發生的兩個併發事件,具備併發的含義,而併發則不必定並行,也亦是說併發事件之間不必定要同一時刻發生。並行通常是指沒有互斥和同步的狀況下獨立進行並同時發生的事件。所以單CPU操做系統的進程/線程嚴格意義上來講都不能算是並行事件,畢竟它們都要使用同一個CPU,真正的並行出如今多處理器的計算機上,當進程/線程獨立運行在不一樣的CPU上,並且不須要共享對象時。粗略的說,沒有資源互斥共享的進程和線程都是並行的。
若是進程/線程沒有共享任何數據,它們編程所關心的許多概念就不會存在。就像現實世界,若是任何兩我的都是獨立的,沒有任何關係的,那麼也不會存在社會的各類機構來協調這些關係。在操做系統中,進程之間共享數據的方式通常經過IO(如:文件、管道、網絡端口等等),固然也有時會經過內存共享。這種鬆耦合的共享形成的同步、互斥問題並很少。常見的同步問題發生同一進程內不一樣線程之間。因爲線程存在於同個進程中,它們之間是能夠共享內存的,因此就會有不少同步和互斥的問題。
那麼什麼是同步,什麼是互斥?同步和互斥每每是共生的。所謂的同步是指不一樣實體的動做按照某些特定條件的順序執行。最多見的莫過於生產者和消費者之間的關係。生產者的生產動做和消費者的消費動做是必須知足前後順序的:只有生產者生產出東西來,消費者才能消費這些東西。它們之間就須要所謂的同步。什麼是互斥?互斥是指兩個實體的動做不容許同時發生,若是同時發生就會產生不能夠預期的結果。互斥是同步的前提,若是兩個動做不是互斥的,就不可能保證其發生的順序。同步必定是互斥的,而互斥不必定須要同步。同步是固定順序的動做的互斥。理解這一點很是重要。
舉個咱們都熟悉的例子,多個併發生產者和多個併發消費者。生產者生產的對象放在一個數組中,而消費者則從這個數組中獲取對象。那麼生產者的生產動做之間是須要互斥的,但不須要同步。不論是A先放在數組,仍是B先放在數組中,它們之間除了爲避免將產品放在數組中同一位置上,須要互斥地訪問數組外,是不須要規定哪個在前面放,哪個在後面放的。它們之間的關係就屬於互斥。而生產者和消費者之間就存在一個前後順序問題,必須至少有生產者生產出產品放在數組中,消費者才能開始消費。所以生產者生產和消費者消費之間的就是同步關係。此外,消費者和消費者之間的消費關係也是須要互斥的,這樣才能避免兩個消費者之間爭奪同數組裏同一位置的對象。可是它們的消費行爲是不須要同步的,只要互斥的進行就好了。
所以不一樣實體之間的動做有兩個基本關係:同步和互斥。通常處理同步的方法是創建在互斥的基礎上的。互斥的機制通常須要經過操做系統甚至底層硬件提供的信號量、管程(管程是創建在信號量基礎上的更高層構件)等底層機制來實現。Java語言中經過提供互斥原語synchronized(雖然叫同步,更準確的說應該是互斥mutex)來保證的,固然Java其實是經過JVM的monitor_enter和monitor_exit指令來實現的,這些指令最終以底層操做系統提供的機制來實現。同步的實現除了要依靠互斥原語,還要結合條件判斷和線程掛起等語言構件來實現。其原理比較簡單,首先要經過原語synchronized互斥兩個須要同步的動做(也稱做臨界代碼),當某個動做(好比消費)得到信號鎖進入管程時,它首先判斷某個條件是否知足(是否有可消費對象),不知足則掛起當前線程,釋放信號鎖,容許其餘線程進入。當其餘線程(好比生產者)進入後,也是檢查是否知足某些條件(好比數組是否有空閒),若是不知足則和前面線程同樣釋放信號鎖並掛起線程,若是知足(有空閒)則進行動做(生產並放在數組空閒處),而後這個動做通常要負責激活其餘掛起的線程(固然也能夠不負責任,其結果是每每形成死鎖),而後本身釋放信號鎖退出管程。其餘被激活的線程進入下一輪競爭,誰得到信號鎖後繼續檢查它須要的條件是否知足,如此繼續下去。html