概述
并發(fā)編程的目的是為了讓程序運行的更快,但并不是啟動更多的線程就能讓程序最大限度地并發(fā)執(zhí)行宁仔。如果想通過多線程使程序運行更快加缘,那么將會面臨很多挑戰(zhàn)。
上下文切換
即使單核處理器也支持多線程執(zhí)行代碼闯第,CPU通過給每個線程分配CPU時間片來實現(xiàn)這個機制。時間片是CPU分配給各個線程的時間缀拭,因為時間短乡括,所以CPU通過不停地切換線程執(zhí)行,讓我們感覺多個線程是同時執(zhí)行的智厌,時間片一般在幾十毫秒诲泌。
CPU在執(zhí)行完一個時間片的任務,要切換到下一個時間片任務時铣鹏,需要先保存當前任務的狀態(tài)敷扫,以便下一次切換回該任務時,再加載這個任務狀態(tài)。所以任務從保存到再加載的過程就是一次上下文的切換葵第。
如何減少上下文的切換
減少上下文切換可以通過無鎖并發(fā)編程绘迁、CAS算法、使用最少線程和使用協(xié)程卒密。
- 無鎖并發(fā)編程:多線程競爭時缀台,會引起上下文切換,所以多線程處理數(shù)據(jù)時哮奇,可以用一些辦法來避免使用鎖膛腐,如將數(shù)據(jù)的ID按照Hash算法取模分段,不同的線程不同段的數(shù)據(jù)鼎俘。
- CAS算法:java的atomic包哲身,使用CAS算法來更新數(shù)據(jù),不需要使用鎖贸伐。
- 使用最少線程勘天。避免創(chuàng)建不需要的線程,比如任務很少捉邢,但是創(chuàng)建了很多線程來處理脯丝,這樣會造成大量線程處于等待狀態(tài)。
- 在單線程里實現(xiàn)多任務的調(diào)度伏伐,并在單線程里維持多個任務的切換巾钉。
死鎖
如果在并發(fā)環(huán)境下,多線程互相競爭共享資源秘案,彼此等待對方釋放鎖,就會導致程序出現(xiàn)問題潦匈,無法繼續(xù)執(zhí)行阱高。那么如何避免死鎖呢?
- 避免一個線程同時獲取多個鎖茬缩;
- 避免一個線程在鎖內(nèi)同時占用多個資源赤惊,盡量保證每個鎖只占用一個資源;
- 嘗試使用定時所凰锡,Lock.tryLock(timeout)來代替使用內(nèi)部鎖機制未舟;
- 對于數(shù)據(jù)庫鎖,加鎖和解鎖必須在同一個數(shù)據(jù)庫連接里掂为,否則會出現(xiàn)解鎖失敗的情況裕膀;
資源限制的挑戰(zhàn)
資源限制是指在進行并發(fā)編程時,程序的執(zhí)行速度受限于計算機硬件資源或軟件資源勇哗。例如昼扛,服務器帶寬只有2Mb/s。某個資源下載速度為1Mb/s欲诺,系統(tǒng)啟動10個線程下載該資源抄谐,下載速度不會編程10Mb/s渺鹦,所以在并發(fā)編程時,需要考慮資源的限制蛹含。硬件資源限制有帶寬限制毅厚、硬盤讀寫速度、CPU處理速度等浦箱。軟件資源限制有數(shù)據(jù)庫連接數(shù)限制和socket連接數(shù)限制吸耿。
在并發(fā)編程中,將代碼執(zhí)行速度加快的原則是將部分串行的代碼變?yōu)椴⑿袌?zhí)行的憎茂。但是如果某段串行代碼并發(fā)執(zhí)行受限于資源珍语,仍然在串行,那么程序的執(zhí)行不僅不會加快竖幔,反而會變慢板乙,因為涉及到上下文切換和資源的調(diào)度。
對于硬件資源限制拳氢,可以考慮集群并行執(zhí)行程序募逞。對于軟件資源的限制,可以考慮使用資源池將資源復用馋评。同時需要根據(jù)不同的資源限制調(diào)整程序的并發(fā)度放接。
注:摘自《Java并發(fā)編程的藝術》