這篇文章的目的並非想教你如何造火箭(面試造火箭,工做擰螺絲),而是想經過對原理和應用案例的有限度剖析來協助你構建起併發的思惟,並將操做系統的理論知識與工程實踐結合起來,貫穿從學到會的全過程。固然,雖然咱們是從實用角度出發,但具備實踐意義的深層次知識點永遠會是面試中的殺手鐗,這可比只能口頭造火箭的理論知識更吸引面試官。面試
本文適合誰:數據庫
在這篇文章中,你將瞭解到併發與多線程相關的一系列概念,經過一些例子咱們能夠在不糾結於具體的技術細節的狀況下造成對併發與多線程相關的各類概念的抽象理解。有了這些概念之後,咱們再去學習具體的理論和技術細節就是手到擒來的事了。編程
最近幾年淘寶發展得如火如荼,涌現出了一大批白手起家的賣家。想象一下你是一個剛剛起步的小賣家,本身運營一個服裝網店,天天都要本身打包發貨。剛開始時生意通常,天天本身一我的一個小時就能幹完。網絡
隨着生意的蓬勃發展,發貨時間慢慢地從一個小時漲到了兩個小時、四個小時,一次由於延遲發貨致使被投訴以後,你終於以爲該招更多的人了。很快,兩個小夥伴加入了你的事業,打包速度開始有了質的提升。這就是最基本的併發了,每一個人均可以當作是一個線程,一樣的工做量,幹得人多了天然就快了。多線程
因此併發
就是經過多個執行器同時執行一個大任務來縮短執行時間、提升執行效率的方法。併發
可是好景不長,週末一盤貨,你發現少了很多。這辦公室裏也沒遭賊,怎麼就會少貨呢?細細一查快遞單,你發現居然有幾單發重了。以後的幾天你都細細留意了一下發貨的過程,最後發現是由於每一個人都會拿着一張發貨清單去備貨,若是有一些訂單不當心打印重複了,就有可能會被不一樣的人重複發貨。雖然數量很少,可是也很心痛啊。這個問題產生的緣由就是由於每一個人在備貨以前拿到的訂單狀態(未發貨)在實際備貨時發生了變化(已由其餘人發貨)。這種對一個共享數據(訂單的發貨狀態)本應獨佔的讀取、檢查、修改過程,若是發生了併發,這種狀況就被稱爲數據競爭
。而這個讀取、檢查、修改的過程就被稱爲臨界區
,臨界區
指的就是一個存在數據競爭
的代碼片斷。分佈式
數據競爭出現的根本緣由是一個數據原本應該只能由一個執行器完整地執行讀取、檢查、修改過程,可是若是出現了併發,那麼就沒辦法保證到了「修改」這一步時的數據還保持了「讀取」時的值了。工具
肯定緣由後,有人想到了一個好辦法,能夠打印一張總的發貨清單,這樣全部人都必須以這個清單上的訂單是否發貨來肯定是否要對訂單進行備貨併發貨了。由於清單隻有一份,因此每次只能由一我的來修改訂單的發貨狀態。這種只能由一個執行器進行數據修改操做來避免發生數據競爭
問題的作法就被稱爲互斥
,也就是咱們常說的鎖
了。oop
由於你管理得當,生意發展得很快,如今的辦公室裏已經堆不下全部衣服了。因此你又租了一個倉庫來一樣進行發貨。兩個地方都會進行發貨,那麼就能夠把每個倉庫理解爲一臺獨立的計算機,這樣經過多臺計算機完成同一任務的方式就能夠被稱爲分佈式
,這樣的一組計算機的集合就被稱爲集羣
。post
這時候以前用一張紙質的總髮貨清單的數據競爭
解決方式就行不通了,因此咱們須要把這張總髮貨清單放到雲端,讓你們能夠經過網絡進行編輯,可是每次只能一我的編輯。在這種狀況下,咱們能夠把兩個倉庫各自當作一臺計算機/進程,而每一個倉庫裏的人就是這個進程中的線程。這樣的話這張總髮貨清單就成爲了一個分佈式鎖
,由於它每次只能有一我的編輯,因此是一個互斥鎖
,或者簡稱爲鎖
;而由於它能夠被兩個進程/計算機(倉庫)共同使用,因此被稱爲是分佈式鎖
。
什麼是進程/線程?
能夠簡單地將進程理解爲咱們電腦/手機上的一個應用,同一臺手機上的每一個App都是一個進程,同一個App在每一個手機上也是一個進程。進程和進程之間能夠理解爲是兩個倉庫,互相之間物理隔離;而線程就是倉庫裏的每個人,他們共享同一個辦公空間。這裏的辦公空間就能夠理解爲操做系統中的虛擬內存空間,可是本文主要討論併發相關的概念,就不繼續展開了。
由於生意比較好,因此全部人都很忙。有時候就會由於有一些人雖然在雲端表格上已經勾上了一個訂單,可是一忙就給忙忘了。其餘人怕重複發貨又不會再去處理已經勾上的訂單了,由於這樣致使的未發貨訂單讓店鋪被投訴了好屢次,影響很是大。這種在併發過程當中修改了數據狀態可是沒有完成後續執行的狀況就會出現數據不一致
,即訂單已經被勾上,但實際並無發貨。
可是做爲聰明的老闆,你又想到了解決的方法。每隔一小時兩個倉庫就會各派一我的檢查一下已經勾上的訂單是否已經都打包完貼上快遞單了。這種每隔一段時間就檢查並處理遺漏的數據不一致
訂單的任務就被稱爲兜底任務
。而經過兜底任務
實現的在最後全部訂單都會達到數據一致狀態的狀況就被稱爲最終一致性
。
你們可能早就以爲前面介紹的總髮貨清單的方法太傻了,只要每一個訂單都只打印一張發貨清單,由單獨一我的去負責分發清單就能夠了,其餘人只要處理好本身被分配到的訂單就能夠了。最後再加上一個兜底任務對訂單的發貨狀況進行二次校驗基本上就不會發生漏發或者重發的狀況了。這種由一個執行器進行任務拆分,再由一組執行器進行處理,最後再由一個或一組執行器進行結果彙總的處理方式就是如今很是流行的map-reduce
方法了。這種方法在大數據或者程序語言標準庫裏都有大量的應用,好比大數據領域赫赫有名的Hadoop和Java語言中的ForkJoinPool
都使用了這種思想。
在這篇文章中,咱們涉及到了如下的技術名詞:
數據競爭
的代碼片斷。數據不一致
狀態的任務。兜底任務
或其餘方式保證數據不一致
的狀況最終會消失。由於併發的知識範圍很大,並且對於一些抽象概念的傳遞必然會須要花費一些篇幅,因此這個主題將會包含一系列文章,主要覆蓋如下主題:
有興趣的讀者能夠繼續關注後續的文章,在以後的文章中會有對併發編程、操做系統原語、硬件原語等等理論與實踐知識的詳細介紹與案例。
對數據庫索引感興趣的讀者能夠了解一下我以前的文章: