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