同步方法調用一旦開始,調用者必須等到方法調用返回後,才繼續執行後續行爲。java
異步方法一旦開始,方法調用會當即返回,調用者就能夠繼續執行後續行爲。而異步方法一般會在另一個線程「真實」地執行,當這個異步調用真實完成時,則會通知調用者。安全
併發偏重於多個任務交替執行,而多個任務之間有可能仍是串行的。並行是真實意義上的「同時執行」。多線程
臨界區用來表示一種公共資源或者說是共享數據,能夠被多個線程使用。可是每一次,只能有一個線程使用它,一旦臨界區資源被佔用,其餘線程要想使用這個資源,就必須等待。併發
阻塞和非阻塞一般用來形容多線程間的相互影響。好比一個線程佔用了臨界區資源,那麼其餘須要這個資源的線程就必須在這個臨界區中等待。等待會致使線程掛起,這種狀況就是阻塞。app
非阻塞的意思與之相反,它強調沒有一個線程能夠妨礙其餘線程執行。全部線程都會嘗試不斷前向執行。異步
死鎖、飢餓和活鎖都屬於多線程的活躍性問題。async
死鎖:A、B、C、D四輛小車在這種狀況下都沒法繼續進行,它們彼此之間都佔用了其餘車輛的車道,若是你們都不肯意釋放本身的車道,那麼這個狀態將永遠維持下去,誰都不可能經過。函數
飢餓:是指某一個或者多個線程由於種種緣由沒法得到所須要的資源,致使一直沒法執行。性能
活鎖:若是兩個或兩個以上的線程都秉承着「謙讓」的原則,主動將資源釋放給他人使用,那麼就會出現資源不斷在兩個線程中跳動,而沒有一個線程能夠同時拿到全部資源而正常執行。這種狀況就是活鎖。優化
根據控制併發的策略,咱們能夠把併發的級別進行分類,大體分爲阻塞、無飢餓、無障礙、無鎖、無等待幾種。
一個線程是阻塞的,那麼在其餘線程釋放資源以前,當前線程沒法繼續執行。
當咱們使用synchronized
或重入鎖時,咱們獲得的就是阻塞的線程。
不管是synchronized
或是重入鎖,都會試圖在執行後續代碼後,獲得臨界區的鎖,若是得不到,線程就會被掛起。
若是線程間有優先級,那麼線程調度優先知足高優先級的線程。對於非公平的鎖來講,系統容許高優先級的線程插隊。這樣有可能致使低優先級產生飢餓。
可是若是鎖時公平的,知足先來後到,那麼飢餓就不會發生。全部線程都有機會執行。
無障礙是一種最弱的非阻塞調度。兩個線程若是是無障礙的執行,那麼他們不會由於臨界區的問題致使一方被掛起。換言之,你們均可以進入臨界區。
若你們一塊兒修改共享數據,數據改壞了怎麼辦呢?對於無障礙的線程來講,一旦檢測到這種狀況,它就會當即對本身所作的修改進行回滾,確保數據安全。
悲觀策略與樂觀策略:若是說阻塞的控制方式是悲觀策略(認爲兩個線程發生衝突的可能性很大),那非阻塞的調度就是一種樂觀策略(認爲兩個線程發生衝突的可能性很小,用,如遇衝突,用回滾的方式解決)。
這個策略可能出如今臨界區發生衝突時,全部線程可能都會不斷回滾本身的操做,而沒有一個線程走出臨界區。
實現無障礙能夠用一個「一致性標記」來實現。
無鎖的並行都是無障礙的。在無鎖的狀況下,全部線程都能嘗試對臨界區進行訪問,但不一樣的是,無鎖的併發保證必然有一個線程可以在有限步內完成操做離開臨界區。
在無鎖的調用中,一個典型特色是可能會包含一個無窮循環。線程會不斷嘗試修改共享變量,無鎖的並行老是能保證一個線程勝出。至於臨界區失敗的線程,它們會不斷重試,直到勝出。若是老是不成功,則可能出現相似飢餓的現象。
無鎖只要求有一個線程能夠在有限步內完成操做,而無等待則在無鎖的基礎上更進一步進行擴展。它要求全部線程都必須在有限步內完成,這樣就不會引發飢餓問題。
爲何要使用並行程序?有兩個目的。第一, 爲了得到更好的性能;第二,因爲業務模型的須要,確實須要多個實體。咱們關注更多的是性能的問題。
加速比定義:加速比 = 優化前系統耗時 / 優化後系統耗時
加速比越高,說明優化效果越好。
JMM的關鍵技術點都是圍繞着多線程的原子性、可見性和有序性來創建的。所以,咱們首先必須瞭解這些概念。
原子性是指一個操做是不可中斷的。即便是多個線程一塊兒執行,一個操做一旦開始,就不會被其餘線程干擾。
int
是原子性的、long
在32位系統來講,不是原子性的。
可見性是指當一個線程修改了某一個共享變量的值,其餘線程是否可以當即知道這個修改。這隻能發生在並行中。
在併發時,程序的執行可能會出現亂序。給人直觀的感受就是:寫在前面的代碼,會在後面執行。有序性問題的緣由是由於程序在執行時,可能進行指令重排,重排後的指令與原指令的順序未必一致。
指令重排能夠保證串行語義一致,可是沒有義務保證多線程間的語義也一致。
爲何要指令重排呢?徹底是爲性能考慮的。具體的緣由能夠翻原書。