多線程的線程開銷


多線程中兩個必要的開銷:線程的創建、上下文切換

創建線程:

創建線程使用是直接向系統申請資源的,對操作系統來說,創建一個線程的代價是十分昂貴的, 需要給它分配內存、列入調度,同時在線程切換的時候還要執行內存換頁,CPU 的緩存被 清空,切換回來的時候還要重新從內存中讀取信息,破壞了數據的局部性。

關於資源:Java線程的線程棧所占用的內存是在Java堆外的,所以是不受java程序控制的,只受系統資源限制,默認一個線程的線程棧大小是1M(當然這個可以通過設置-Xss屬性設置,但是要注意棧溢出問題),但是,如果每個用戶請求都新建線程的話,1024個用戶光線程就占用了1個G的內存,如果系統比較大的話,一下子系統資源就不夠用了,最后程序就崩潰了。

同樣的道理在java程序中也不要隨意開啟新的線程,特別是高頻業務盡量使用線程池,不然很容易導致內存不足,程序崩潰的問題。


 上下文切換:

概念:
當前任務執行一個時間片后會切換到下一個任務。在切換之前,上一個任務的狀態會被保存下來,下次切換回這個任務時,可以再加載這個任務的狀態,任務從保存到再加載的過程就是一次上下文切換。

說明:
1)時間片是CPU分配給各個線程的時間,時間片一般是幾十毫秒。
2)CPU通過給每個線程分配CPU時間片,並且不停地切換線程來實現多線程。因為時間片非常短,所以感覺多個線程是在同時執行。

減少上下文切換的方法:

1)無鎖並發編程:
多線程競爭鎖時,會引起上下文切換,所以在使用多線程處理數據時,可以采用一些策略來避免使用鎖。
常見的策略:將數據按照id的哈希值進行切分,不同的線程處理不同段的數據。

2)鎖分離技術
舉例:ConcurrentHashMap

3)CAS算法
java的Atomic包使用CAS算法來更新數據,而不需要加鎖。

4)使用最少的線程
避免創建不需要的線程,比如任務很少,但是創建了很多線程來處理,這樣會造成大量線程都處於等待狀態。


舉例:

通過減少大量WAITING的線程,來減少上下文切換次數

# 轉儲堆棧信息
jstack PID > dumpfile

# 統計所有線程的狀態
grep java.lang.Thread.State dumpfile | awk '{print $2" "$3" "$4" "$5}' | sort | uniq -c

如果存在大量waiting的線程,則查看dumpfile文件進行分析:
1)如果是服務器的工作線程大量等待,則修改服務器配置文件中線程池的配置信息,然后重啟查看效果。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM