Java 併發全局梳理
概述
隨着摩爾定律逐步失效,CPU 單核性能達到瓶頸,併發逐漸逐漸得到廣泛應用,因而學習瞭解以及使用併發就顯得十分重要。但併發相關的知識比較瑣碎,不易系統學習,因而本篇文章參照王寶令老師《Java 併發編程》來勾勒出一張 “併發全景圖”。
是什麼?
併發:同一時間段,多個任務都在執行 (單位時間內不一定同時執行);
說到 併發
,不得不提就是 並行
:
並行:單位時間內,多個任務同時執行。
兩者大眼一看很像,仔細一想卻並不相同,因爲 並行
強調某個時間點多個任務同時執行,而 併發
強調的是一個時間段內多個任務都在執行。
怎麼做?
大部分併發問題,最終都可以抽象成三類問題分工、同步和互斥。而且針對不同的問題有着不同的方式來解決,具體如下圖所示:
分工
所謂 分工
,類似於現實中一個組織完成一個項目,項目經理要拆分任務,安排合適的成員去完成。
在併發編程領域,你就是項目經理,線程就是項目組成員。任務分解和分工對於項目成敗非常關鍵,不過在併發領域裏,分工更重要,它直接決定了併發程序的性能,並且分工非常重要且複雜,因而 Java 併發包中有一系列方法來實現 分工
:
-
"Executor 與線程池"
-
"ForkJoin"
-
"Future 的使用"
基於分工思想設計的併發設計模式也有很多:
-
"Guarded Supension 模式"
-
"Balking 模式"
-
"Threa-Per-Message 模式"
-
"生產者 - 消費者模式"
-
"Work Thread 模式"
-
"兩階段終止模式"
同步
而 同步
更多描述的是一種協同關係,在分完工之後,具體執行時,任務之間會有依賴,一個任務之後完成之後,其他依賴它的任務才能開始進行,因而就引入的 同步
來協同各個任務之間的執行順序。
針對該類問題,Java 也提供了一系列工具來輔助解決:
-
"信號量(Semaphore)機制"
-
"管程(Monitor)"
-
"CountDownLatch"
-
"CyclicBarrier"
-
"Phaser"
-
"Exchanger"
互斥
分工、同步主要爲了充分發掘 CPU 的性能來解決問題,但併發問題中,還需要解決正確性問題,即保證 線程安全
。
當多個線程同時訪同一個變量時,最後執行的結果是不確定的,比如下邊這段代碼:
1public class UnsafeSequence {
2 private int value = 0;
3 public int getNext() {
4 return ++value;
5 }
6}
如果我們有多個線程同時調用 getNext()
時,多個線程之間推進順序的不同可能會有不同的執行結果:
可能會是 2,遞推順序如下:
可能結果是 1,代碼執行順序如下:
因而結果是不確定的,也就是說結果可能是正確的(比如上邊的程序執行結果爲 2),可能是錯誤的(比如執行結果是 1),執行前是不知道的。而導致不確定的主要源頭主要是三個問題可見性問題、有序性問題和原子性問題,爲了解決這三個問題,Java 引入了內存模型,內存模型提供一系列規則利用這些規則,我們可以避免可見性問題、有序性問題,但是還不足以完全解決線程安全問題。解決線程安全問題的核心方案還是 互斥
。
所謂互斥,指的是同一時刻,只允許一個線程訪問共享變量。
實現互斥主要手段是互斥鎖主要包含下邊這些手段:
-
Synchronized
-
Lock
-
讀寫鎖
除此之外,未來提高速度,也有一些無鎖的方案:
-
不變模式
-
線程本地存儲
-
CAS
-
Copy - on - Write
-
原子類
總結
本文主要從分工、同步和互斥三類問題展開,從解決對應問題角度出發大致梳理了 Java 併發知識的學習前景圖。後續將分若干部分來講對應的內容。
引用
-
《深入理解 Java 虛擬機》
-
《Java 併發實戰》
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/-BXvlGAdkmtYRtGX7TM5TA