java 中的多線程在代碼層面是通過 new 多個(gè) Thread 或是線程池實(shí)現(xiàn)的,這只是表象,我們需要繼續(xù)審圖探究。最終的計(jì)算任務(wù)是由 CPU 完成的,那么我們只要知道 CPU 如何調(diào)度 Thread 的就行了,這就引出了 CPU 時(shí)間這個(gè)概念
無論是單核還是多核 CPU 都是支持多線程的,CPU 通過給每個(gè)線程分配 CPU 時(shí)間片來實(shí)這個(gè)機(jī)制,這個(gè)時(shí)間片非常短,所以 CPU 通過不停地切換線程執(zhí)行,讓我們感覺多個(gè)線程是同時(shí)執(zhí)行的,時(shí)間片一般是幾十毫秒(ms)
然后我們?cè)傧胍幌耄覀冇?jì)算的數(shù)據(jù)存在內(nèi)存,但是 CPU 不在內(nèi)存中計(jì)算數(shù)據(jù),而是在 CPU 所屬的寄存器中計(jì)算任務(wù),這就涉及到 內(nèi)存和 CPU 寄存器相互交互數(shù)據(jù)了。一個(gè)時(shí)間片段很短,很大可能一個(gè)線程的任務(wù)完不成,但是在這個(gè) CPU 時(shí)間段結(jié)束后由別的線程搶到了隱形機(jī)會(huì),那么 CPU 就得執(zhí)行別的線程的任務(wù),此時(shí)上一個(gè)線程未完成的任務(wù)的數(shù)據(jù)需要保存起來,然后加載下一個(gè)任務(wù)的數(shù)據(jù)進(jìn)來,這就是線程上下問的切換
java 的這種多線程機(jī)制由于涉及線程的上下文操作會(huì)帶來一個(gè)性能問題,線程的上下文切換和線程對(duì)象的創(chuàng)建是很消耗資源的
2個(gè)加減法循環(huán)在串行和并行時(shí)的性能對(duì)比,只有在百萬(wàn)次時(shí)他們的性能在相容,百萬(wàn)次以下,并行沒有性能上的優(yōu)勢(shì),所以在 rxjava 的線程池線程設(shè)計(jì)在大型就算任務(wù)時(shí),線程池線程數(shù)量 =cpu 內(nèi)核數(shù),io 等非計(jì)算耗時(shí)任務(wù)時(shí)線程池線程數(shù)量無顯示
那么如何減少上下文切換:
-
無鎖并發(fā)編程
無鎖并發(fā)編程.當(dāng)任何特定的運(yùn)算被阻塞的時(shí)候,所有CPU可以繼續(xù)處理其他的運(yùn)算。換種方式說,在無鎖系統(tǒng)中,當(dāng)給定線程被其他線程阻塞的時(shí)候,所有CPU可以不停的繼續(xù)處理其他工作。無鎖算法大大增加系統(tǒng)整體的吞吐量,因?yàn)樗慌紶枙?huì)增加一定的交易延遲。大部分高端數(shù)據(jù)庫(kù)系統(tǒng)是基于無鎖算法而構(gòu)造的,以滿足不同級(jí)別 -
CAS 算法
Java提供了一套原子性操作的數(shù)據(jù)類型(java.util.concurrent.atomic包下),使用CAS算法來更新數(shù)據(jù),不需要加鎖。如:AtomicInteger、AtomicLong等 -
使用最少線程
避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多線程來處理,這樣會(huì)造成大量線程都處于等待狀態(tài) -
協(xié)程
在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個(gè)任務(wù)間的切換,kotlin ,goland 都支持協(xié)程