1. 程式人生 > >多執行緒與高併發程式設計進階(一)

多執行緒與高併發程式設計進階(一)

前言:

使用多執行緒的目的:
充分利用CPU資源,提高程式執行速度
使用多執行緒面臨的挑戰:
上下文切換、死鎖、計算機軟硬體資源的限制等問題
結論:
不是一味地開啟執行緒就能夠讓程式最大限度地併發執行,以及提升執行速度,想利用多執行緒提升程式執行速度需要結合實際情況考慮上下文切換、死鎖以及軟硬體資源限制等因素,只有這樣才能夠合理地使用多執行緒來提升程式執行效率

上下文切換:

通過為每個執行緒分配CPU時間片來實現多執行緒執行程式碼的機制,即使在單核處理器的情況下,依舊可以使用多執行緒執行程式碼;

時間片就是CPU的執行時間,通過分配時間片給各個執行緒,用以執行對應的程式碼;CPU時間片一般很短(幾十毫秒左右),通過不停地切換執行緒來使用對應的CPU時間片,這樣帶來的感覺就是這些執行緒似乎在並行執行;

由於一個執行緒分配得到的CPU時間片用完,需要切換下一個執行緒繼續使用CPU的計算資源,但是在這之前,執行緒需要將當前的狀態進行儲存,以便下次再次獲得CPU時間片時可以載入對應的狀態以繼續執行剩下的任務(程式碼),所以在儲存執行緒的當前狀態到載入下一個執行緒的狀態這個過程就是一次上下文切換;注意:上下文切換需要耗費時間,會影響多執行緒程式的執行效率,在使用多執行緒時需要考慮這一點;

減少上下文切換的途徑:

  1. 無鎖併發程式設計:鎖的競爭會帶來執行緒上下文的切換
  2. CAS演算法:CAS演算法在資料更新方面,可以達到鎖的效果
  3. 使用最少執行緒:避免不必要的執行緒等待
  4. 使用協程:單執行緒完成多工的排程和切換,避免多執行緒

無鎖併發程式設計,避免由於使用鎖導致的上下文切換,比如對共享資料進行分段,不同的執行緒處理不同段的資料;比如JDK提供的ConcurrentHashMap;
CAS演算法,比如Java的Atomic包中的原子類使用CAS演算法來更新資料的狀態,從而避免了鎖的使用;
使用最少執行緒,即不建立不需要的執行緒,按需使用執行緒,避免過多不必要的執行緒等待(等待獲取鎖);
使用協程,在單執行緒中進行多工的排程以及維持多個任務之間的切換(實質上已經和多執行緒無關);

死鎖:

多個執行緒互相等待已經被對方執行緒正在佔用的鎖,導致陷入彼此等待對方釋放鎖(但是又都不釋放已佔用的鎖)的狀態(死鎖),最終會導致系統功能的不可用;

避免死鎖的常見措施:

  1. 避免一個執行緒中同時獲取多個鎖
  2. 避免一個執行緒在一個鎖中獲取其他的鎖資源
  3. 考慮使用定時鎖來替換內部鎖機制,如lock.tryLock(timeout),避免由於異常發生導致的鎖釋放失敗的情況發生
  4. 對於資料庫鎖,需要在同一個資料庫連線中完成同一個鎖的獲取與釋放,避免出現釋放鎖失敗的情況

計算機資源的限制:

由於計算機上的硬體、軟體資源的限制,導致多執行緒並不能發揮優勢,相反會對程式執行效能帶來影響;

常見的限制有:

  1. 計算機的頻寬、上傳下載速度以及硬碟讀寫速度、CPU的處理速度等硬體資源上的限制
  2. 資料庫連線數、socket連線數等軟體資源上的限制

解決的方法:

  1. 使用資料庫連線池以及socket連結池等技術來降低建立連線的時間以及提高連線的複用率
  2. 根據不同的資源限制調整程式的併發度(執行緒數量)根據不同的資源限制調整程式的併發度(執行緒數量)

建議:
儘量使用JDK提供的併發容器以及併發工具類來解決併發問題,這些JDK中的併發工具和容器是經過優化和驗證的,所以一般不會出現上述的問題;