Java 併發全局梳理

概述

隨着摩爾定律逐步失效,CPU 單核性能達到瓶頸,併發逐漸逐漸得到廣泛應用,因而學習瞭解以及使用併發就顯得十分重要。但併發相關的知識比較瑣碎,不易系統學習,因而本篇文章參照王寶令老師《Java 併發編程》來勾勒出一張 “併發全景圖”。

是什麼?

併發:同一時間段,多個任務都在執行 (單位時間內不一定同時執行);

說到 併發,不得不提就是 並行

並行:單位時間內,多個任務同時執行。

兩者大眼一看很像,仔細一想卻並不相同,因爲 並行 強調某個時間點多個任務同時執行,而 併發 強調的是一個時間段內多個任務都在執行

怎麼做?

大部分併發問題,最終都可以抽象成三類問題分工同步互斥。而且針對不同的問題有着不同的方式來解決,具體如下圖所示:

分工

所謂 分工,類似於現實中一個組織完成一個項目,項目經理要拆分任務,安排合適的成員去完成。

在併發編程領域,你就是項目經理,線程就是項目組成員。任務分解和分工對於項目成敗非常關鍵,不過在併發領域裏,分工更重要,它直接決定了併發程序的性能,並且分工非常重要且複雜,因而 Java 併發包中有一系列方法來實現 分工

基於分工思想設計的併發設計模式也有很多:

同步

而 同步 更多描述的是一種協同關係,在分完工之後,具體執行時,任務之間會有依賴,一個任務之後完成之後,其他依賴它的任務才能開始進行,因而就引入的 同步 來協同各個任務之間的執行順序。

針對該類問題,Java 也提供了一系列工具來輔助解決:

互斥

分工、同步主要爲了充分發掘 CPU 的性能來解決問題,但併發問題中,還需要解決正確性問題,即保證 線程安全

當多個線程同時訪同一個變量時,最後執行的結果是不確定的,比如下邊這段代碼:

1public class UnsafeSequence {
2	private int value = 0;
3	public int getNext() {
4		return ++value;
5	}
6}

如果我們有多個線程同時調用 getNext() 時,多個線程之間推進順序的不同可能會有不同的執行結果:

可能會是 2,遞推順序如下:

可能結果是 1,代碼執行順序如下:

因而結果是不確定的,也就是說結果可能是正確的(比如上邊的程序執行結果爲 2),可能是錯誤的(比如執行結果是 1),執行前是不知道的。而導致不確定的主要源頭主要是三個問題可見性問題、有序性問題和原子性問題,爲了解決這三個問題,Java 引入了內存模型,內存模型提供一系列規則利用這些規則,我們可以避免可見性問題、有序性問題,但是還不足以完全解決線程安全問題。解決線程安全問題的核心方案還是 互斥

所謂互斥,指的是同一時刻,只允許一個線程訪問共享變量。

實現互斥主要手段是互斥鎖主要包含下邊這些手段:

除此之外,未來提高速度,也有一些無鎖的方案:

總結

本文主要從分工、同步和互斥三類問題展開,從解決對應問題角度出發大致梳理了 Java 併發知識的學習前景圖。後續將分若干部分來講對應的內容。

引用

  1. 《深入理解 Java 虛擬機》

  2. 《Java 併發實戰》

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-BXvlGAdkmtYRtGX7TM5TA