關(guān)于Java多線程上下文切換的總結(jié)
什么是上下文切換
即使是單核CPU也支持多線程執(zhí)行代碼,CPU通過給每個線程分配CPU時間片來實(shí)現(xiàn)這個機(jī)制。時間片是CPU分配給各個線程的時間,因?yàn)闀r間片非常短,所以CPU通過不停地切換線程執(zhí)行,讓我們感覺多個線程時同時執(zhí)行的,時間片一般是幾十毫秒(ms)。
CPU通過時間片分配算法來循環(huán)執(zhí)行任務(wù),當(dāng)前任務(wù)執(zhí)行一個時間片后會切換到下一個任務(wù)。但是,在切換前會保存上一個任務(wù)的狀態(tài),以便下次切換回這個任務(wù)時,可以再次加載這個任務(wù)的狀態(tài),從任務(wù)保存到再加載的過程就是一次上下文切換。
這就像我們同時讀兩本書,當(dāng)我們在讀一本英文的技術(shù)書籍時,發(fā)現(xiàn)某個單詞不認(rèn)識,于是便打開中英文詞典,但是在放下英文書籍之前,大腦必須先記住這本書讀到了多少頁的第多少行,等查完單詞之后,能夠繼續(xù)讀這本書。這樣的切換是會影響讀書效率的,同樣上下文切換也會影響多線程的執(zhí)行速度。
上下文切換代碼測試
下面的代碼演示串行和并發(fā)執(zhí)行并累加操作的時間:
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)行的時間測試結(jié)果:
從表中可以看出,100次并發(fā)執(zhí)行累加以下,串行執(zhí)行和并發(fā)執(zhí)行的運(yùn)行速度總體而言差不多,1萬次以下串行執(zhí)行甚至還可以說是略快。為什么并發(fā)執(zhí)行的速度會比串行慢呢?這就是因?yàn)?strong>線程有創(chuàng)建和上下文切換的開銷。
引起線程上下文切換的原因
對于我們經(jīng)常使用的搶占式操作系統(tǒng)而言,引起線程上下文切換的原因大概有以下幾種:
- 當(dāng)前執(zhí)行任務(wù)的時間片用完之后,系統(tǒng)CPU正常調(diào)度下一個任務(wù)
- 當(dāng)前執(zhí)行任務(wù)碰到IO阻塞,調(diào)度器將此任務(wù)掛起,繼續(xù)下一任務(wù)
- 多個任務(wù)搶占鎖資源,當(dāng)前任務(wù)沒有搶到鎖資源,被調(diào)度器掛起,繼續(xù)下一任務(wù)
- 用戶代碼掛起當(dāng)前任務(wù),讓出CPU時間
- 硬件中斷
如何減少上下文切換
既然上下文切換會導(dǎo)致額外的開銷,因此減少上下文切換次數(shù)便可以提高多線程程序的運(yùn)行效率。減少上下文切換的方法有無鎖并發(fā)編程、CAS算法、使用最少線程和使用協(xié)程。
- 無鎖并發(fā)編程。多線程競爭時,會引起上下文切換,所以多線程處理數(shù)據(jù)時,可以用一些辦法來避免使用鎖,如將數(shù)據(jù)的ID按照Hash取模分段,不同的線程處理不同段的數(shù)據(jù)
- CAS算法。Java的Atomic包使用CAS算法來更新數(shù)據(jù),而不需要加鎖
- 使用最少線程。避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多線程來處理,這樣會造成大量線程都處于等待狀態(tài)
- 協(xié)程。在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個任務(wù)間的切換
到此這篇關(guān)于關(guān)于Java多線程上下文切換的總結(jié)的文章就介紹到這了,更多相關(guān)Java多線程上下文切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java根據(jù)實(shí)體生成SQL數(shù)據(jù)庫表的示例代碼
這篇文章主要來和大家分享一個Java實(shí)現(xiàn)根據(jù)實(shí)體生成SQL數(shù)據(jù)庫表的代碼,文中的實(shí)現(xiàn)代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-07-07Java實(shí)現(xiàn)讀取163郵箱,qq郵箱的郵件內(nèi)容
這篇文章主要利用Java語言實(shí)現(xiàn)讀取163郵箱和qq郵箱的郵件內(nèi)容,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起動手試一試2022-02-02java編程實(shí)現(xiàn)簡單的網(wǎng)絡(luò)爬蟲示例過程
這篇文章主要為大家介紹了如何使用java編程實(shí)現(xiàn)一個簡單的網(wǎng)絡(luò)爬蟲示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10Java 根據(jù)網(wǎng)址查詢DNS/IP地址的方法
這篇文章主要介紹了Java 根據(jù)網(wǎng)址查詢DNS/IP地址的方法,具體實(shí)現(xiàn)代碼,大家參考下本文2017-12-12