上下文切換
單核處理器是如何支持多線程編程的踢关? CPU通過給每個(gè)線程分配CPU時(shí)間片來實(shí)現(xiàn)勺馆。
- 時(shí)間片是CPU分配給各個(gè)線程的時(shí)間,時(shí)間片非常短拗秘,CPU通過不停切換線程執(zhí)行圣絮,切換時(shí)間很快,讓人感覺是在同時(shí)執(zhí)行雕旨。
- CPU通過時(shí)間片算法來循環(huán)執(zhí)行任務(wù)扮匠,當(dāng)前任務(wù)執(zhí)行完一個(gè)時(shí)間片后會(huì)切換到下一個(gè)任務(wù),切換前輝保存上一個(gè)任務(wù)的狀態(tài)凡涩,便于下次切回的時(shí)候可以再加載這個(gè)任務(wù)的狀態(tài)棒搜。任務(wù)從保存到再加載的過程就是一次上下文切換。
- 因?yàn)榫€程有創(chuàng)建和上下文的切換的開銷活箕,并發(fā)執(zhí)行累加操作次數(shù)沒有很高的時(shí)候力麸,速度會(huì)比串行執(zhí)行慢。
測(cè)試上下文切換次數(shù)和時(shí)長(zhǎng)的工具
- 使用Lmbench3可以測(cè)量上下文切換的時(shí)長(zhǎng)
- 使用vmstat可以測(cè)量上下文切換的次數(shù)
如何減少上下文切換
- 無鎖并發(fā)編程。多線程競(jìng)爭(zhēng)鎖時(shí)克蚂,會(huì)引起上下文切換闺鲸,可以用些方法來避免用鎖。比如對(duì)數(shù)據(jù)id計(jì)算取模分段埃叭,不同線程處理不同段的數(shù)據(jù)摸恍。
- CAS算法。CAS:Compare and Swap, 翻譯成比較并交換赤屋。
java.util.concurrent
包中借助CAS實(shí)現(xiàn)了區(qū)別于synchronouse同步鎖的一種樂觀鎖立镶。 - 使用最少線程。避免創(chuàng)建不需要的線程益缎。比如任務(wù)很少谜慌,不比創(chuàng)建大量線程,可能會(huì)導(dǎo)致大量線程處于等待狀態(tài)莺奔。
- 協(xié)程:在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度欣范,并在單線程里維持多個(gè)任務(wù)間的切換。具體java如何實(shí)現(xiàn)協(xié)程要找個(gè)時(shí)間研究下令哟。
死鎖
dump分析死鎖
- 一旦出現(xiàn)死鎖恼琼,業(yè)務(wù)是可感知的,不能繼續(xù)提供服務(wù)屏富。只能通過dump線程查看是哪個(gè)線程出現(xiàn)了問題晴竞。
- Jstack是JDK自帶的命令行工具,主要用于線程Dump分析
-
我們先用Jps來查看java進(jìn)程id(或者Linux的ps命令)
-
jstack的使用
-
jstack輸出線程dump信息到文件
-
查看文件狠半,關(guān)鍵字是at DeadThread.run噩死,比如at DeadThread.run(DeadThread.java:37),說明Thread-1實(shí)在DeadThread類的37行處發(fā)生死鎖
避免死鎖的常見方法
- 避免一個(gè)線程同時(shí)獲取多個(gè)鎖神年。
- 避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源已维,盡量保證每個(gè)鎖只占用一個(gè)資源。
- 嘗試使用定時(shí)鎖已日,使用
lock.tryLock(timeout)
來替代使用內(nèi)部鎖機(jī)制垛耳。 - 對(duì)于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫連接里飘千,否則會(huì)出現(xiàn)解鎖失效的情況堂鲜。