Java實現(xiàn)多線程的上下文切換
對于上下文切換不同的操作系統(tǒng)模式也不盡相同,這里我們只討論Unix系統(tǒng),在我之前的文章中提到過windows的搶占式,這里就不在贅述。
無論是單核還是多核CPU都是支持多線程代碼的,CPU通過給每個線程分配CPU時間片來實這個機制。
時間片是CPU分配給各個線程的時間,因為時間片非常短,所以CPU通過不停地切換線程執(zhí)行,讓我們感覺多個線程是同時執(zhí)行的,時間片一般是幾十毫秒(ms)
CPU通過時間片分配算法來循環(huán)執(zhí)行任務(wù),當(dāng)前任務(wù)執(zhí)行一個時間片后會切換到下一個任務(wù)。但是,在切換前會保存上一個任務(wù)的狀態(tài),以便下次切換回這個任務(wù)時,可以再加載這個任務(wù)的狀態(tài)。
所以任務(wù)從保存到再加載的過程就是一次上下文切換。 很明顯上下文切換會影響多線程的執(zhí)行速度。
如何減少上下文切換
減少上下文切換的方法有
1、無鎖并發(fā)編程。
多線程競爭鎖時,會引起上下文切換,所以多線程處理數(shù)據(jù)時,可以用一
些辦法來避免使用鎖,如將數(shù)據(jù)的ID按照Hash算法取模分段,不同的線程處理不同段的數(shù)據(jù)。
2、CAS算法。
Java的Atomic包使用CAS(compare and swap)算法來更新數(shù)據(jù),而不需要加鎖。
3、使用最少線程。避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多線程來處理,這
樣會造成大量線程都處于等待狀態(tài)。
4、協(xié)程:在單線程里實現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個任務(wù)間的切換。
減少上下文切換的例子
下面我們看一個通過減少線上大量WAITING的線程,來減少上下文切換次數(shù)的例子:
使用jstack命令dump線程信息,看看pid為3117的進程里的線程都在做什么
sudo -u admin /opt/java/bin/jstack 31177 > /home/java/dump17
統(tǒng)計所有線程分別處于什么狀態(tài),發(fā)現(xiàn)300多個線程處于WAITING(onobjectmonitor)狀態(tài)
grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'
| sort | uniq -c
39 RUNNABLE
21 TIMED_WAITING(onobjectmonitor)
6 TIMED_WAITING(parking)
51 TIMED_WAITING(sleeping)
305 WAITING(onobjectmonitor)
3 WAITING(parking)
打開dump文件查看處于WAITING(onobjectmonitor)的線程在做什么。發(fā)現(xiàn)這些線程基本全是JBOSS的工作線程,在await。說明JBOSS線程池里線程接收到的任務(wù)太少,大量線程都閑著。
"http-0.0.0.0-7001-97" daemon prio=10 tid=0x000000004f6a8000 nid=0x555e in Object.wait() [0x0000000052423000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007969b2280> (a org.apache.tomcat.util.net.AprEndpoint$Worker) at java.lang.Object.wait(Object.java:485) at org.apache.tomcat.util.net.AprEndpoint$Worker.await(AprEndpoint.java:1464) - locked <0x00000007969b2280> (a org.apache.tomcat.util.net.AprEndpoint$Worker) at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1489) at java.lang.Thread.run(Thread.java:662)
減少JBOSS的工作線程數(shù),找到JBOSS的線程池配置信息,將maxThreads降到100
<maxThreads="250" maxHttpHeaderSize="8192" emptySessionPath="false" minSpareThreads="40" maxSpareThreads="75" maxPostSize="512000" protocol="HTTP/1.1" enableLookups="false" redirectPort="8443" acceptCount="200" bufferSize="16384" connectionTimeout="15000" disableUploadTimeout="false" useBodyEncodingForURI= "true">
重啟JBOSS,再dump線程信息,然后統(tǒng)計WAITING(onobjectmonitor)的線程,發(fā)現(xiàn)減少了175個。
WAITING的線程少了,系統(tǒng)上下文切換的次數(shù)就會少,因為每一次從WAITTING到RUNNABLE都會進行一次上下文的切換。
讀者也可以使用vmstat命令測試一下。
grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5}'
| sort | uniq -c
44 RUNNABLE
22 TIMED_WAITING(onobjectmonitor)
9 TIMED_WAITING(parking)
36 TIMED_WAITING(sleeping)
130 WAITING(onobjectmonitor)
1 WAITING(parking)
為什么要減少上下文切換
當(dāng)CPU從執(zhí)行一個線程切換到執(zhí)行另外一個線程的時候,它需要先存儲當(dāng)前線程的本地的數(shù)據(jù),程序指針等,然后載入另一個線程的本地數(shù)據(jù),程序指針等,最后才開始執(zhí)行。
這種切換稱為“上下文切換”(“context switch”)。
CPU會在一個上下文中執(zhí)行一個線程,然后切換到另外一個上下文中執(zhí)行另外一個線程。上下文切換并不廉價,是比較耗時的
以上這篇Java實現(xiàn)多線程的上下文切換就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于apache poi根據(jù)模板導(dǎo)出excel的實現(xiàn)方法
下面小編就為大家?guī)硪黄赼pache poi根據(jù)模板導(dǎo)出excel的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
使用BeanFactory實現(xiàn)創(chuàng)建對象
這篇文章主要為大家詳細介紹了使用BeanFactory實現(xiàn)創(chuàng)建對象,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08
Spring注解驅(qū)動擴展原理BeanFactoryPostProcessor
這篇文章主要介紹了Spring注解驅(qū)動擴展原理BeanFactoryPostProcessor,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03
SpringBoot2.x中management.security.enabled=false無效的解決
這篇文章主要介紹了SpringBoot2.x中management.security.enabled=false無效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Java中使用DOM4J生成xml文件并解析xml文件的操作
這篇文章主要介紹了Java中使用DOM4J來生成xml文件和解析xml文件的操作,今天通過代碼給大家展示了解析xml文件和生成xml文件的方法,需要的朋友可以參考下2021-09-09
通過java備份恢復(fù)mysql數(shù)據(jù)庫的實現(xiàn)代碼
這篇文章主要介紹了如何通過java備份恢復(fù)mysql數(shù)據(jù)庫,其實一般情況下通過bat或sh就可以,這里主要是介紹了java的實現(xiàn)思路,喜歡的朋友可以參考下2013-09-09
Java設(shè)計模式之中介者模式(Mediator Pattern)簡介
這篇文章主要介紹了Java設(shè)計模式之中介者模式(Mediator Pattern),需要的朋友可以參考下2014-07-07
Spring Boot 中常用的注解@RequestParam及基本用法
@RequestParam 是 Spring Framework 和 Spring Boot 中常用的注解之一,用于從請求中獲取參數(shù)值,本文給大家介紹Spring Boot 中常用的注解@RequestParam,感興趣的朋友一起看看吧2023-10-10

