關(guān)于Java多線程上下文切換的總結(jié)
什么是上下文切換
即使是單核CPU也支持多線程執(zhí)行代碼,CPU通過(guò)給每個(gè)線程分配CPU時(shí)間片來(lái)實(shí)現(xiàn)這個(gè)機(jī)制。時(shí)間片是CPU分配給各個(gè)線程的時(shí)間,因?yàn)闀r(shí)間片非常短,所以CPU通過(guò)不停地切換線程執(zhí)行,讓我們感覺(jué)多個(gè)線程時(shí)同時(shí)執(zhí)行的,時(shí)間片一般是幾十毫秒(ms)。
CPU通過(guò)時(shí)間片分配算法來(lái)循環(huán)執(zhí)行任務(wù),當(dāng)前任務(wù)執(zhí)行一個(gè)時(shí)間片后會(huì)切換到下一個(gè)任務(wù)。但是,在切換前會(huì)保存上一個(gè)任務(wù)的狀態(tài),以便下次切換回這個(gè)任務(wù)時(shí),可以再次加載這個(gè)任務(wù)的狀態(tài),從任務(wù)保存到再加載的過(guò)程就是一次上下文切換。
這就像我們同時(shí)讀兩本書(shū),當(dāng)我們?cè)谧x一本英文的技術(shù)書(shū)籍時(shí),發(fā)現(xiàn)某個(gè)單詞不認(rèn)識(shí),于是便打開(kāi)中英文詞典,但是在放下英文書(shū)籍之前,大腦必須先記住這本書(shū)讀到了多少頁(yè)的第多少行,等查完單詞之后,能夠繼續(xù)讀這本書(shū)。這樣的切換是會(huì)影響讀書(shū)效率的,同樣上下文切換也會(huì)影響多線程的執(zhí)行速度。
上下文切換代碼測(cè)試
下面的代碼演示串行和并發(fā)執(zhí)行并累加操作的時(shí)間:
public class ContextSwitchTest { private static final long count = 10000; public static void main(String[] args) throws Exception { concurrency(); serial(); } private static void concurrency() throws Exception { long start = System.currentTimeMillis(); Thread thread = new Thread(new Runnable(){ public void run() { int a = 0; for (int i = 0; i < count; i++) { a += 5; } } }); thread.start(); int b = 0; for (long i = 0; i < count; i++) { b --; } thread.join(); long time = System.currentTimeMillis() - start; System.out.println("Concurrency:" + time + "ms, b = " + b); } private static void serial() { long start = System.currentTimeMillis(); int a = 0; for (long i = 0; i < count; i++) { a += 5; } int b = 0; for (int i = 0; i < count; i++) { b --; } long time = System.currentTimeMillis() - start; System.out.println("Serial:" + time + "ms, b = " + b + ", a = " + a); } }
修改上面的count值,即修改循環(huán)次數(shù),看一下串行運(yùn)行和并發(fā)運(yùn)行的時(shí)間測(cè)試結(jié)果:
從表中可以看出,100次并發(fā)執(zhí)行累加以下,串行執(zhí)行和并發(fā)執(zhí)行的運(yùn)行速度總體而言差不多,1萬(wàn)次以下串行執(zhí)行甚至還可以說(shuō)是略快。為什么并發(fā)執(zhí)行的速度會(huì)比串行慢呢?這就是因?yàn)?strong>線程有創(chuàng)建和上下文切換的開(kāi)銷(xiāo)。
引起線程上下文切換的原因
對(duì)于我們經(jīng)常使用的搶占式操作系統(tǒng)而言,引起線程上下文切換的原因大概有以下幾種:
- 當(dāng)前執(zhí)行任務(wù)的時(shí)間片用完之后,系統(tǒng)CPU正常調(diào)度下一個(gè)任務(wù)
- 當(dāng)前執(zhí)行任務(wù)碰到IO阻塞,調(diào)度器將此任務(wù)掛起,繼續(xù)下一任務(wù)
- 多個(gè)任務(wù)搶占鎖資源,當(dāng)前任務(wù)沒(méi)有搶到鎖資源,被調(diào)度器掛起,繼續(xù)下一任務(wù)
- 用戶代碼掛起當(dāng)前任務(wù),讓出CPU時(shí)間
- 硬件中斷
如何減少上下文切換
既然上下文切換會(huì)導(dǎo)致額外的開(kāi)銷(xiāo),因此減少上下文切換次數(shù)便可以提高多線程程序的運(yùn)行效率。減少上下文切換的方法有無(wú)鎖并發(fā)編程、CAS算法、使用最少線程和使用協(xié)程。
- 無(wú)鎖并發(fā)編程。多線程競(jìng)爭(zhēng)時(shí),會(huì)引起上下文切換,所以多線程處理數(shù)據(jù)時(shí),可以用一些辦法來(lái)避免使用鎖,如將數(shù)據(jù)的ID按照Hash取模分段,不同的線程處理不同段的數(shù)據(jù)
- CAS算法。Java的Atomic包使用CAS算法來(lái)更新數(shù)據(jù),而不需要加鎖
- 使用最少線程。避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多線程來(lái)處理,這樣會(huì)造成大量線程都處于等待狀態(tài)
- 協(xié)程。在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個(gè)任務(wù)間的切換
到此這篇關(guān)于關(guān)于Java多線程上下文切換的總結(jié)的文章就介紹到這了,更多相關(guān)Java多線程上下文切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java根據(jù)實(shí)體生成SQL數(shù)據(jù)庫(kù)表的示例代碼
這篇文章主要來(lái)和大家分享一個(gè)Java實(shí)現(xiàn)根據(jù)實(shí)體生成SQL數(shù)據(jù)庫(kù)表的代碼,文中的實(shí)現(xiàn)代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-07-07Java實(shí)現(xiàn)讀取163郵箱,qq郵箱的郵件內(nèi)容
這篇文章主要利用Java語(yǔ)言實(shí)現(xiàn)讀取163郵箱和qq郵箱的郵件內(nèi)容,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起動(dòng)手試一試2022-02-02java編程實(shí)現(xiàn)簡(jiǎn)單的網(wǎng)絡(luò)爬蟲(chóng)示例過(guò)程
這篇文章主要為大家介紹了如何使用java編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)爬蟲(chóng)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10Java獲取視頻時(shí)長(zhǎng)及截取幀截圖詳解
這篇文章主要介紹了Java獲取視頻時(shí)長(zhǎng)及截取幀截圖詳解,以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。,需要的朋友可以參考下2019-06-06Feign?請(qǐng)求動(dòng)態(tài)URL方式
這篇文章主要介紹了Feign?請(qǐng)求動(dòng)態(tài)URL方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07Java 根據(jù)網(wǎng)址查詢DNS/IP地址的方法
這篇文章主要介紹了Java 根據(jù)網(wǎng)址查詢DNS/IP地址的方法,具體實(shí)現(xiàn)代碼,大家參考下本文2017-12-12