更新時間:2020-11-20 17:56:49 來源:動力節(jié)點 瀏覽2857次
對于單核 CPU,CPU 在一個時刻只能運行一個線程,當(dāng)在運行一個線程的過程中轉(zhuǎn)去運行另外一個線程,這個叫做線程上下文切換。
由于可能當(dāng)前線程的任務(wù)并沒有執(zhí)行完畢,所以在切換時需要保存線程的運行狀態(tài),以便下次重新切換回來時能夠繼續(xù)切換之前的狀態(tài)運行。舉個簡單的例子:比如一個線程A正在讀取一個文件的內(nèi)容,正讀到文件的一半,此時需要暫停線程A,轉(zhuǎn)去執(zhí)行線程B,當(dāng)再次切換回來執(zhí)行線程A的時候,我們不希望線程A又從文件的開頭來讀取。
因此需要記錄線程A的運行狀態(tài),那么會記錄哪些數(shù)據(jù)呢?因為下次恢復(fù)時需要知道在這之前當(dāng)前線程已經(jīng)執(zhí)行到哪條指令了,所以需要記錄程序計數(shù)器的值,另外比如說線程正在進(jìn)行某個計算的時候被掛起了,那么下次繼續(xù)執(zhí)行的時候需要知道之前掛起時變量的值時多少,因此需要記錄CPU寄存器的狀態(tài)。所以一般來說,線程上下文切換過程中會記錄程序計數(shù)器、CPU寄存器狀態(tài)等數(shù)據(jù)。
簡而言之:對于線程的上下文切換實際上就是 存儲和恢復(fù)CPU狀態(tài)的過程,它使得線程執(zhí)行能夠從中斷點恢復(fù)執(zhí)行。
既然上下文切換會帶來開銷,給CPU帶來負(fù)擔(dān),那么我們該如何減少線程上下文切換呢?
1 .減少線程的數(shù)量
由于一個CPU每個時刻只能執(zhí)行一條線程,而傲嬌的我們又想讓程序并發(fā)執(zhí)行,操作系統(tǒng)只好不斷地進(jìn)行上下文切換來使我們從感官上覺得程序是并發(fā)執(zhí)的行。因此,我們只要減少線程的數(shù)量,就能減少上下文切換的次數(shù)。然而如果線程數(shù)量已經(jīng)少于CPU核數(shù),每個CPU執(zhí)行一條線程,照理來說CPU不需要進(jìn)行上下文切換了,但事實并非如此。
2 .控制同一把鎖上的線程數(shù)量
如果多條線程共用同一把鎖,那么當(dāng)一條線程獲得鎖后,其他線程就會被阻塞;當(dāng)該線程釋放鎖后,操作系統(tǒng)會從被阻塞的線程中選一條執(zhí)行,從而又會出現(xiàn)上下文切換。因此,減少同一把鎖上的線程數(shù)量也能減少上下文切換的次數(shù)。
3 .采用無鎖并發(fā)編程
需要并發(fā)執(zhí)行的任務(wù)是無狀態(tài)的:HASH分段
所謂無狀態(tài)是指并發(fā)執(zhí)行的任務(wù)沒有共享變量,他們都獨立執(zhí)行。對于這種類型的任務(wù)可以按照ID進(jìn)行HASH分段,每段用一條線程去執(zhí)行。
需要并發(fā)執(zhí)行的任務(wù)是有狀態(tài)的:CAS算法
如果任務(wù)需要修改共享變量,那么必須要控制線程的執(zhí)行順序,否則會出現(xiàn)安全性問題。你可以給任務(wù)加鎖,保證任務(wù)的原子性與可見性,但這會引起阻塞,從而發(fā)生上下文切換;為了避免上下文切換,你可以使用CAS算法,僅在線程內(nèi)部需要更新共享變量時使用CAS算法來更新,這種方式不會阻塞線程,并保證更新過程的安全性。
因此,盡管多線程可以使得任務(wù)執(zhí)行的效率得到提升,但由于在線程切換時同樣會帶來一定的開銷代價,并且多個線程會導(dǎo)致系統(tǒng)資源占用的增加,所以在進(jìn)行多線程編程時要注意這些因素。好了,線程上下文切換就講到這里,想要掌握更多的多線程知識的小伙伴抓緊時間攻克本站的Java多線程教程吧!