高並發編程-03-並發編程存在的風險
1,線程的安全性問題
假設,我現在編寫了一個程序,來保存系統的訪問量,如何實現呢?
很簡單,我可以設置一個全局變量,然後,每次用戶訪問一次系統,我就自增1,那麽這個程序應該是這樣寫的,如下:
public class System {
private int count;
public int getCount(){
return count++;
}
}
這段代碼,大家覺得有沒有問題?
如果在單線程的模式下,沒問題,但是在多線程的模式下,會有問題,為什麽?
因為其中一個關鍵的步驟,count++不是一個原子操作,這個操作實際會分為三個步驟,
分別是,讀取count值,給count值+1,將結果寫到count值上。
那麽大家想想,當我們有多個線程同時訪問這個資源的時候,會出現什麽情況?
這就是我們說的線程安全問題。
如果解決此類問題,java給我們提供一種同步機制,你只需要這麽做就可以解決問題:
public synchronized int getCount(){
return count++;
}
好了,接下來,我們來說會出現的第二類問題
2,活躍性問題
比如,死鎖,饑餓,活鎖,這些都是屬於活躍性問題。當某個操作無法繼續執行時,這就是活躍性問題,我舉個死鎖的例子,比如我老婆跟我分工家務活,要求我先拖地,她就洗碗,而我要求她先洗碗,我再拖地,那麽這個時候,我們彼此手握對方進行下一步工作的開啟條件,但都彼此不願意先動手,於是就陷入了僵局,這就是死鎖。
換成程序的說法,就是A線程需要等待的鎖被B線程占用,而B線程需要的等待的鎖被A線程占用,所以相互都不釋放,於是就陷入了死鎖。
更多問題,我們後續繼續講解
3,性能問題
性能問題,你肯定會有疑問,多線程不就是為了提高系統的吞吐量嗎?怎麽還有性能問題?
這個原因很簡單,還是那句話,你沒有正確使用
下面我們來看看多線程可能帶來的性能問題有哪些?
當我們采用多線程編程時,每個線程會由CPU實現調度,分配一定的時間片,那這個時候,調度器就會頻繁掛起一個活躍的線程轉而運行另一個線程,這就是我們說的“上下文切換”,這種操作將會帶來極大的開銷。
還有一個問題,當CPU重新切換回之前活躍的線程時,線程是否要繼續之前未完成的操作,答案是必須的,那麽就需要做一件事,在CPU切換的時候,保存線程的執行狀態,這又是一筆開銷。
最後,還有一種情況,就是多線程共享有狀態的數據時,需要使用同步機制來保證數據安全,而這些機制會抑制編譯器的某些優化,比如使得內存緩沖區中的數據失效等等,以及增加共享內存總線的同步流量
所以,有個重要的問題,一般我們的程序開啟多少線程合適?
當然不是越多越好,大家可以留言,說說你的答案
高並發編程-03-並發編程存在的風險