多線程的操作原則
1. 前言
本節內容主要是對多線程的操作原則程進行講解,具體內容點如下:
- 了解多線程 AVO 原則,是學習本節內容的基礎;
- 了解單 CPU 時代的多線程,能夠更好地理解多 CPU 誕生的原因;
- 了解多 CPU 時代的多線程,是目前我們接觸和使用到的開發場景;
- 了解為什么要進行多線程并發,使我們學習本套課程的前提,也是多線程并發的意義所在;
- 了解線程安全問題,這是本節課程的重點,安全問題是開發過程中非常重要且不容忽視的問題;
- 了解共享變量內存可見性問題,是本節的重點之一。
2. 多線程 AVO 原則
A:即 Atomic,原子性操作原則。對基本數據類型的變量讀和寫是保證原子性的,要么都成功,要么都失敗,這些操作不可中斷。
V:即 volatile,可見性原則。后續的小節會對 volatile 關鍵字進行深入的講解,此處只需要理解概念性問題即可。使用 volatile 關鍵字,保證了變量的可見性,到主存拿數據,不是到緩存里拿。
O:即 order, 就是有序性。代碼的執行順序,在代碼編譯前的和代碼編譯后的執行順序不變。
3. 單 CPU 時代的多線程
概念:單核 CPU 上,同一時刻只能有一條線程運行,單核 CPU 上運行的單線程程序和多線程程序,從運行效率上看沒有差別。換而言之,單 CPU 時代,沒有真正的多線程并發效果,從這一點來看,多線程與 CPU 硬件的升級息息相關。
單 CPU 時代下的多線程:在單 CPU 時代多任務是共享一個 CPU 的,當一個任務占用 CPU 運行時,其他任務就會被掛起,當占用 CPU 的任務時間片用完后,會把 CPU 讓給其他任務來使用,所以在單 CPU 時代多線程編程是沒有太大意義的,并且線程間頻繁的上下文切換還會帶來額外開銷。
上圖所示為在單個 CPU 上運行兩個線程,線程 A 和線程 B 是輪流使用 CPU 進行任務處理的,也就是在某個時間內單個 CPU 只執行一個線程上面的任務。當線程 A 的時間片用完后會進行線程上下文切換,也就是保存當前線程 A 的執行上下文,然后切換到線程 B 來占用 CPU 運行任務。
4. 多 CPU 時代的多線程
如下圖所示為雙 CPU 配置,線程 A 和線程 B 各自在自己的 CPU 上執行任務,實現了真正的并行運行。
在多線程編程實踐中,線程的個數往往多于 CPU 的個數,所以一般都稱多線程并發編程而不是多線程并行編程。
5. 為什么要進行多線程并發
意義:多核 CPU 時代的到來打破了單核 CPU 對多線程效能的限制。 多個 CPU 意味著每個線程可以使用自己的 CPU 運行,這減少了線程上下文切換的開銷。
但隨著對應用系統性能和吞吐量要求的提高,出現了處理海量數據和請求的要求,這些都對高并發編程有著迫切的需求。
6. 線程安全問題
談到線程安全問題,我們先說說什么是共享資源。
共享資源:所謂共享資源,就是說該資源被多個線程所持有或者說多個線程都可以去訪問該資源。線程安全問題是指當多個線程同時讀寫一個共享資源并且沒有任何同步措施時,導致出現臟數據或者其他不可預見的結果和問題。
對于線程安全問題,在進行實際的開發操作過程中,我們要分析一下幾點內容,確保多線程環境下的線程安全問題。
- 確定是否是多線程環境:多線程環境下操作共享變量需要考慮線程的安全性;
- 確定是否有增刪改操作:多線程環境下,如果對共享數據有增加,刪除或者修改的操作,需要謹慎。為了保證線程的同步性,必須對該共享數據進行加鎖操作,保證多線程環境下,所有的線程能夠獲取到正確的數據。如生產者與消費者模型,售票模型;
- 多線程下的讀操作:如果是只讀操作,對共享數據不需要進行鎖操作,因為數據本身未發生增刪改操作,不會影響獲取數據的準確性。
7. 共享變量內存可見性問題
先來看下共享變量和內存可見性的定義。
共享變量:非線程私有的變量,共享變量存放于主內存中,所有的線程都有權限對變量進行增刪改查操作。
內存可見性:由于數據是存放于內存中的,內存可見性意味著數據是公開的,所有線程都可對可見性的數據進行增刪改查操作。
Java 內存模型規定,將所有的變量都存放在主內存(共享內存)中,當線程使用變量時,會把主內存里面的變量復制到自己的工作空間或者叫作工作內存,也就是我們所說的線程私有內存,線程讀寫變量時操作的是自己工作內存中的變量。
當一個線程操作共享變量時,它首先從主內存復制共享變量到自己的工作內存,然后對工作內存里的變量進行處理,處理完后將變量值更新到主內存。
總結:對于內存可見的共享變量,在對其進行操作時,一定要注意線程的安全問題,保證線程的安全以及數據的準確性,是多線程并發編程的重點。
8. 小結
多線程環境下進行并發編程,需要遵循多線程的并發原則,這是我們學習本節內容的基礎所在。本節的重點在于對線程安全問題的關注以及共享變內存可見性操作問題的考慮。
本節內容為我們后續進行的多線程并發的內容奠定了良好的理論基礎。