1.上下文切換
CPU通過時間片分配算法來循環(huán)執(zhí)行任務(wù)间驮,當(dāng)前任務(wù)執(zhí)行一個時間片后會切換到下一個任務(wù)。但是马昨,在切換前會保存上一個任務(wù)的狀態(tài)竞帽,以便下次切換回這個任務(wù)時,可以再加載這個任務(wù)的狀態(tài)鸿捧。所以任務(wù)從保存到再加載的過程就是一次上下文切換屹篓。
①如何減少上下文切換
減少上下文切換的方法有無鎖并發(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é)程:在單線程里實現(xiàn)多任務(wù)的調(diào)度嘉蕾,并在單線程里維持多個任務(wù)間的切換。
②減少上下文切換案例
a.用jstack命令dump線程信息霜旧,看看pid為3117的進程里的線程都在做什么错忱。
sudo -u admin /opt/ifeve/java/bin/jstack 3117 > /home/tengfi.fangtf/dump17
b.統(tǒng)計所有線程分別處于什么狀態(tài),發(fā)現(xiàn)300多個線程處于WAITING(on object monitor)狀態(tài)颁糟。
grep java.lang.Thread.State dump17 | awk '{print $2$3$4$5} | sort | uniq -c'
c.打開dump文件查看處于WAITING(on object monitor)的線程都在做什么航背。發(fā)現(xiàn)這些線程基本全是Jboss的工作線程喉悴,在await棱貌。說明Jboss線程池里線程接收到的任務(wù)太少,大量線程都閑著箕肃。
d.減少Jboss的工作線程數(shù)婚脱,找到Jboss的線程池配置信息,將maxThreads降到100(原250)勺像。
e.重啟Jboss障贸,再dump線程信息,然后統(tǒng)計WAITING(on object monitor)的線程吟宦,發(fā)現(xiàn)減少了175個篮洁。WAITING的線程少了,系統(tǒng)上下文切換的次數(shù)就會少殃姓,因為每一次從WAITING到RUNNABLE都會進行一次上下文的切換袁波。
2.死鎖
在一些復(fù)雜的場景中,可能會遇到這樣的問題蜗侈,比如t1拿到鎖之后篷牌,因為一些異常情況沒有釋放鎖(死循環(huán))。又或者是t1拿到了一個數(shù)據(jù)庫鎖踏幻,釋放鎖的時候拋出了異常枷颊,沒有釋放掉。
Thread t1 = new Thread(() -> {
synchronized(A){
...
synchronized(B){
...
}
}
});
Thread t2 = new Thread(() -> {
synchronized(B){
...
synchronized(A){
...
}
}
});
一旦出現(xiàn)死鎖,業(yè)務(wù)是可感知的夭苗,因為不能繼續(xù)提供服務(wù)了信卡,那么只能通過dump線程查看到底是哪個線程出現(xiàn)了問題,一下線程信息告訴我們是DeadLockDemo類的第42行和第31行引起的死鎖听诸。
避免死鎖的幾個常見方法
a.避免一個線程同時獲取多個鎖
b.避免一個線程在鎖內(nèi)同時占用多個資源坐求,盡量保證每個鎖只占用一個資源。
c.嘗試使用定時鎖晌梨,使用lock.tryLock(timeout)來替代使用內(nèi)部鎖機制桥嗤。
d.對于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個數(shù)據(jù)庫連接里仔蝌,否則會出現(xiàn)解鎖失敗的情況泛领。
3.資源限制的挑戰(zhàn)
①什么是資源限制
資源限制是指在進行并發(fā)編程時,程序的執(zhí)行速度受限于計算機硬件資源或軟件資源敛惊。
例如帶寬只有2Mb/s渊鞋,某個資源的下載速度是1Mb/s,系統(tǒng)啟動10個線程并不會變成10Mb/s瞧挤。
硬件資源限制有:帶寬的上傳/下載速度锡宋、硬盤讀寫速度和CPU的處理速度。
軟件資源限制有:數(shù)據(jù)庫的連接數(shù)和socket連接數(shù)等特恬。
②資源限制引發(fā)的問題
將某段串行的代碼并發(fā)執(zhí)行,但因為受限于資源癌刽,仍然在串行執(zhí)行役首,這時候程序不僅不會加快執(zhí)行,反而會更慢显拜,因為增加了上下文切換和資源調(diào)度的時間衡奥。
③如何解決資源限制的問題
對于硬件資源限制,可以考慮使用集群并行執(zhí)行程序远荠。
對于軟件資源限制矮固,可以考慮使用資源池資源復(fù)用。比如使用連接池將數(shù)據(jù)庫和socket連接復(fù)用譬淳,或者在調(diào)用對方WebService接口獲取數(shù)據(jù)時档址,只建立一個連接。
④在資源限制情況下進行并編程
如何在資源限制的情況下瘦赫,讓程序執(zhí)行的更快呢辰晕?方法就是,根據(jù)不同的資源限制調(diào)整程序的并發(fā)度确虱。比如下載文件程序依賴于帶寬和硬盤讀寫速度含友。有數(shù)據(jù)庫操作時,涉及數(shù)據(jù)庫連接數(shù),如果SQL語句執(zhí)行非尘轿剩快辆童,而線程的數(shù)量比數(shù)據(jù)庫連接數(shù)大很多,則某些線程會被阻塞惠赫,等待數(shù)據(jù)庫連接把鉴。
4.小結(jié)
強烈建議多使用jdk并發(fā)包提供的并發(fā)容器和工具類來解決并發(fā)問題,因為這些類都已經(jīng)通過了充分的測試和優(yōu)化儿咱,均可解決了上面提到的幾個挑戰(zhàn)庭砍。