併發編程的挑戰

Java併發編程的藝術筆記

併發編程的目的與挑戰

併發編程的目的是爲了讓程序運行得更快。啓動更多的線程並不一定就能讓程序最大限度地併發執行。
希望通過多線程執行任務讓程序運行得更快,會面臨非常多的挑戰。比如

  • 上下文切換 的問題
  • 死鎖 的問題
  • 硬件和軟件的資源限制問題

上下文切換

單核處理器也支持多線程執行代碼,CPU通過給每個線程分配CPU時間片來實現這個機制。時間片是CPU分配給各個線程的時間,因爲時間片非常短,所以CPU通過不停地切換線程執行,讓我們感覺多個線程是同時執行的,時間片一般是幾十毫秒(ms)。

CPU通過時間片分配算法來循環執行任務,當前任務執行一個時間片後會切換到下一個任務。但是,在切換前會保存上一個任務的狀態,以便下次切換回這個任務時,可以再加載這個任務的狀態。所以任務從保存到再加載的過程就是一次上下文切換。

上下文切換也會影響多線程的執行速度。

如何減少上下文切換

減少上下文切換的方法有 無鎖併發編程、CAS算法、使用最少線程 和 使用協程。

  • 無鎖併發編程。多線程競爭鎖時,會引起上下文切換,所以多線程處理數據時,可以用一些辦法來避免使用鎖,如將數據的ID按照Hash算法取模分段,不同的線程處理不同段的數據。
  • CAS算法。Java的Atomic包使用CAS算法來更新數據,而不需要加鎖。
    使用最少線程。避免創建不需要的線程,比如任務很少,但是創建了很多線程來處理,這樣會造成大量線程都處於等待狀態。
  • 協程:在單線程裏實現多任務的調度,並在單線程裏維持多個任務間的切換。

避免死鎖的幾個常見方法

  • 避免一個線程同時獲取多個鎖。
  • 避免一個線程在鎖內同時佔用多個資源,儘量保證每個鎖只佔用一個資源。
  • 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制。
    對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏,否則會出現解鎖失敗的情況。

資源限制的挑戰

  • 硬件資源限制有帶寬的上傳/下載速度、硬盤讀寫速度和CPU的處理速度。
  • 軟件資源限制有數據庫的連接數和socket連接數等。

如何解決資源限制的問題

  • 對於硬件資源限制,可以考慮使用集羣並行執行程序。比如使用ODPS、Hadoop或者自己搭建服務器集羣。
  • 對於軟件資源限制,可以考慮使用資源池將資源複用。比如使用連接池將數據庫和Socket連接複用,或者在調用對方webservice接口獲取數據時,只建立一個連接。

在資源限制情況下進行併發編程

根據不同的資源限制調整程序的併發度,比如下載文件程序依賴於兩個資源——帶寬和硬盤讀寫速度。有數據庫操作時,涉及數據庫連接數,如果SQL語句執行非常快,而線程的數量比數據庫連接數大很多,則某些線程會被阻塞,等待數據庫連接。

如果覺得不錯的話,請幫忙點個讚唄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章