Java併發編程總結

背景

  • 計算機由CPU、存儲器、輸入輸出設備組成。
  • CPU內部又包括了控制器、運算器、寄存器(存儲器)
  • 其中進程是計算機資源分配的最小單位,線程是程序執行的最小單位(CPU資源調度的最小單位) 。
  • CPU通過給每個線程分配CPU時間片來達到併發執行的效果。
  • 每個線程得到的時間片都很短,一般是幾十毫秒(ms)
  • CPU通過不停地切換線程,讓我們感覺多個線程在同時執行

併發編程帶來的挑戰

  • 當CPU切換線程時,需要保存上一個線程任務的狀態,加載當前線程任務的狀態,即上下文切換
  • 上下文切換的過程會消耗少量CPU資源的,非常頻繁的切換反而會浪費CPU資源
  • 減少上下文切換的方法
    • 無鎖併發編程:避免使用鎖,如將數據的ID按照Hash算法取模分段,不同的線程處理不同段的數據
    • CAS算法:Java的Atomic包使用CAS算法來更新數據,而不需要加鎖
    • 使用最少線程:避免創建多餘的線程,避免大量線程都處於等待狀態的情況發生。
    • 協程:在單線程裏實現多任務的調度,並在單線程裏維持多個任務間的切換。
  • 死鎖是指多個進程在運行中因爭奪資源造成的一種僵局,此時若無外力作用,它們都將無法再向前推進
  • 產生死鎖的4個必要條件
    • 互斥條件:進程要求對所分配的資源進行排它性控制,即在一段時間內某資源僅爲一進程所佔用。
    • 請求和保持條件:當進程因請求資源而阻塞時,對已獲得的資源保持不放。
    • 不剝奪條件:進程已獲得的資源在未使用完之前,不能剝奪,只能在使用完時由自己釋放。
    • 環路等待條件:在發生死鎖時,必然存在一個進程–資源的環形鏈。
  • 避免死鎖的幾個方法:
    • 避免一個線程同時獲取多個鎖。
    • 避免一個線程在鎖內同時佔用多個資源,儘量保證每個鎖只佔用一個資源。
    • 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制。
    • 對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏,否則會出現解鎖失敗的情況
  • 資源限制:在進行併發編程時,程序的執行速度受限於計算機硬件資源或軟件資源(cpu、磁盤、帶寬)
  • 解決資源限制的問題
    • 對於硬件資源限制,可以考慮使用集羣並行執行程序
    • 軟件資源限制,可以考慮使用資源池將資源複用

Java中的幾種IO模型

  • Linux的虛擬地址空間也爲0~4G。
  • 將最高的1G字節(虛擬地址 0xC0000000~0xFFFFFFFF),供內核使用,稱爲內核空間
  • 而將較低的3G字節(虛擬地址 0x00000000~0xBFFFFFFF),供各個進程使用,稱爲用戶空間
  • 內核空間中存放的是內核代碼和數據,而進程的用戶空間中存放的是用戶程序的代碼和數據。
  • 內核空間和用戶空間之間一般通過系統調用進行通訊
  • 進程在執行用戶自己的代碼時,一般稱其處於用戶態,此時處理器特權級最低(3級)
  • 當一個進程執行系統調用而陷入內核代碼中執行時,一般稱進程處於內核態(0級)
    • CPU運行於進程上下文時,內核代表進程運行於內核空間
    • CPU運行於中斷上下文時,內核代表硬件運行於內核空間
    • 進程上下文是用戶進程傳遞給內核的一些參數以及內核要保存的寄存器值和當時的環境
    • 中斷上下文是硬件傳遞過來的一些參數和內核需要保存的進程環境
  • 用戶態內核態之間進行切換時同樣耗費CPU資源,尤其是IO處理時會出現數據的重複拷貝
  • 標準IO
    • 標準IO讀寫會發生4次數據拷貝(DMA拷貝和內存拷貝),4次上下文切換(用戶態/內核態)
    • 讀:數據從磁盤讀取到內核緩存,然後從緩存讀取數據
    • 寫:數據從用戶地址空間複製到內核地址空間的緩存中,系統會自己決定何時將緩存寫入磁盤
  • 直接IO
    • 讀:數據從磁盤讀取到用戶程序自己的緩存中,然後從緩存讀取數據
    • 寫:數據寫入用戶程序自己的內存中緩存,然後由用戶程序寫入磁盤,一般都是數據庫管理系統
  • 同步IO:寫入數據時,一直阻塞到當數據從緩存刷入磁盤時成功後
  • 異步IO:訪問數據的線程發起請求後繼續處理其他的事情,當數據返回後再繼續處理
  • 內存映射:關聯磁盤的空間地址與內存,將訪問內存轉爲直接訪問文件的某一部分。省去了複製的過程
  • 零拷貝
    • 使用native方法transferTo0()實現,直接在內核空間完成文件讀取並轉到磁盤,使用sendfile系統調用
    • 效率高但無法操作文件內容,2次數據拷貝(0次CPU拷貝),2次上下文切換
  • NIO:零拷貝技術;直接內存映射:MappedByteBuffer將文件映射到堆外內存中,有內存泄露的可能

Java併發編程模型

  • 常用同步原語

    • synchronize
    • volatile
      • 通過內存屏障指令來禁止特定類型的處理器重排序
    • final
  • 原子操作

  • 操作系統線程狀態:新建,運行中,就緒,等待,結束

  • java線程狀態:新建,可運行(運行中+就緒),阻塞+等待+超時等待(等待),結束

  • 線程之間的通信:一般有共享內存消息傳遞兩種方式。Java的併發採用的是共享內存模型。

  • 線程之間的同步:同步是指程序中用於控制不同線程間操作發生相對順序的機制

  • JMM了決定一個線程對共享變量的寫入何時對另一個線程可見。(主內存+)

  • 線程之間的共享變量存儲在主內存中,每個線程都有一個私有的本地內存,本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存涵蓋了緩存、寫緩衝區、寄存器以及其他的硬件和編譯器優化

  • 內存可見性:在JMM中(JSR-133),一個操作執行的結果要對另一個操作可見時,這兩個操作間就是存在happens-before關係。

  • 順序一致性,所有操作必須按照程序的順序來執行,且每個操作都必須原子執行且立刻對所有線程可見。

未完待續!

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