進(jìn)程間上下文切換
鎖的競(jìng)爭(zhēng)太激烈會(huì)導(dǎo)致鎖升級(jí)為重量級(jí)鎖筷屡,未搶到鎖的線(xiàn)程會(huì)進(jìn)入monitor捷兰,而monitor依賴(lài)于底層操作系統(tǒng)的mutex lock,獲取鎖時(shí)會(huì)發(fā)生用戶(hù)態(tài)和內(nèi)核態(tài)之間的切換,所以會(huì)發(fā)生進(jìn)程間的上下文切換冗美。
線(xiàn)程間上下文切換
線(xiàn)程間上下文切換就是一個(gè)工作的線(xiàn)程被另外一個(gè)線(xiàn)程暫停昼钻,另外一個(gè)線(xiàn)程占用了處理器開(kāi)始執(zhí)行任務(wù)的過(guò)程掸屡。系統(tǒng)和 Java 程序自發(fā)性以及非自發(fā)性的調(diào)用操作,就會(huì)導(dǎo)致上下文切換然评,從而帶來(lái)系統(tǒng)開(kāi)銷(xiāo)仅财。線(xiàn)程越多,系統(tǒng)的運(yùn)行速度不一定越快碗淌。那么我們平時(shí)在并發(fā)量比較大的情況下盏求,什么時(shí)候用單線(xiàn)程,什么時(shí)候用多線(xiàn)程呢亿眠?一般在單個(gè)邏輯比較簡(jiǎn)單碎罚,而且速度相對(duì)來(lái)非常快的情況下纳像,我們可以使用單線(xiàn)程荆烈。例如Redis,從內(nèi)存中快速讀取值爹耗,不用考慮 I/O 瓶頸帶來(lái)的阻塞問(wèn)題耙考。而在邏輯相對(duì)來(lái)說(shuō)很復(fù)雜的場(chǎng)景,等待時(shí)間相對(duì)較長(zhǎng)又或者是需要大量計(jì)算的場(chǎng)景潭兽,我建議使用多線(xiàn)程來(lái)提高系統(tǒng)的整體性能倦始。例如,NIO 時(shí)期的文件讀寫(xiě)操作山卦、圖像處理以及大數(shù)據(jù)分析等鞋邑。
上下文
上下文都包括哪些內(nèi)容呢?具體來(lái)說(shuō)账蓉,它包括了寄存器的存儲(chǔ)內(nèi)容以及程序計(jì)數(shù)器存儲(chǔ)的指令內(nèi)容枚碗。CPU 寄存器負(fù)責(zé)存儲(chǔ)已經(jīng)、正在和將要執(zhí)行的任務(wù)铸本,程序計(jì)數(shù)器負(fù)責(zé)存儲(chǔ) CPU 正在執(zhí)行的指令位置以及即將執(zhí)行的下一條指令的位置
原因
結(jié)合圖示可知肮雨,線(xiàn)程主要有“新建”(NEW)、“就緒”(RUNNABLE)箱玷、“運(yùn)行”(RUNNING)怨规、“阻塞”(BLOCKED)陌宿、“死亡”(DEAD)五種狀態(tài)。到了 Java 層面它們都被映射為了 NEW波丰、RUNABLE壳坪、BLOCKED、WAITING掰烟、TIMED_WAITING爽蝴、TERMINADTED 等 6 種狀態(tài)。
- 在這個(gè)運(yùn)行過(guò)程中纫骑,線(xiàn)程由 RUNNABLE 轉(zhuǎn)為非 RUNNABLE 的過(guò)程就是線(xiàn)程上下文切換蝎亚。
- 一個(gè)線(xiàn)程的狀態(tài)由 RUNNING 轉(zhuǎn)為 BLOCKED ,再由 BLOCKED 轉(zhuǎn)為 RUNNABLE 先馆,然后再被調(diào)度器選中執(zhí)行颖对,這就是一個(gè)上下文切換的過(guò)程。
- 當(dāng)一個(gè)線(xiàn)程從 RUNNING 狀態(tài)轉(zhuǎn)為 BLOCKED 狀態(tài)時(shí)磨隘,我們稱(chēng)為一個(gè)線(xiàn)程的暫停,線(xiàn)程暫停被切出之后顾患,操作系統(tǒng)會(huì)保存相應(yīng)的上下文番捂,以便這個(gè)線(xiàn)程稍后再次進(jìn)入 RUNNABLE 狀態(tài)時(shí)能夠在之前執(zhí)行進(jìn)度的基礎(chǔ)上繼續(xù)執(zhí)行。
- 當(dāng)一個(gè)線(xiàn)程從 BLOCKED 狀態(tài)進(jìn)入到 RUNNABLE 狀態(tài)時(shí)江解,我們稱(chēng)為一個(gè)線(xiàn)程的喚醒设预,此時(shí)線(xiàn)程將獲取上次保存的上下文繼續(xù)完成執(zhí)行。
- 通過(guò)線(xiàn)程的運(yùn)行狀態(tài)以及狀態(tài)間的相互切換犁河,我們可以了解到鳖枕,多線(xiàn)程的上下文切換實(shí)際上就是由多線(xiàn)程兩個(gè)運(yùn)行狀態(tài)的互相切換導(dǎo)致的。那么在線(xiàn)程運(yùn)行時(shí)桨螺,線(xiàn)程狀態(tài)由 RUNNING 轉(zhuǎn)為 BLOCKED 或者由 BLOCKED 轉(zhuǎn)為 RUNNABLE宾符,這又是什么誘發(fā)的呢?
在 Linux 系統(tǒng)下灭翔,可以使用 Linux 內(nèi)核提供的 vmstat 命令魏烫,來(lái)監(jiān)視 Java 程序運(yùn)行過(guò)程中系統(tǒng)的上下文切換頻率,cs 如下圖所示:
如果是監(jiān)視某個(gè)應(yīng)用的上下文切換肝箱,就可以使用 pidstat 命令監(jiān)控指定進(jìn)程的 Context Switch 上下文切換
至于系統(tǒng)開(kāi)銷(xiāo)具體發(fā)生在切換過(guò)程中的哪些具體環(huán)節(jié)哄褒,總結(jié)如下:
- 操作系統(tǒng)保存和恢復(fù)上下文;
- 調(diào)度器進(jìn)行線(xiàn)程調(diào)度煌张;
- 處理器高速緩存重新加載呐赡;
- 上下文切換也可能導(dǎo)致整個(gè)高速緩存區(qū)被沖刷,從而帶來(lái)時(shí)間開(kāi)銷(xiāo)骏融。