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