創(chuàng)建多少線程合適竿裂?
多線程本質(zhì)上是提升多核CPU的利用率,所以根據(jù)使用情況分為:計算密集型搭盾、io密集型玩郊。
一個通用公式(僅供參考):
- I/O密集型:
最佳線程數(shù)=1 +(I/O耗時 / CPU耗時)
- CPU密集型:
最佳線程數(shù)=CPU核數(shù) * [ 1 +(I/O耗時 / CPU耗時)]
這個只能做為參考绘沉,真正的項目中要依賴壓測赃绊,重點關(guān)注CPU、I/O設(shè)備的利用率和性能指標(biāo)(響應(yīng)時間守问、吞吐量)之間的關(guān)系(IO耗時是可以通過apm 工具來計算)匀归。
如果說是集群部署,那么線程數(shù)就根據(jù)自己的機器的核心參數(shù)來設(shè)置耗帕。
方法是如何被執(zhí)行的
具體示例:
int a = 7穆端;
int[] b = fibonacci(a);
int[] c = b;
''''''''
int[] fibonacci(int n) {
// 創(chuàng)建結(jié)果數(shù)組
int[] r = new int[n];
return r;
}
當(dāng)你調(diào)用fibonacci(a)的時候,CPU要先找到方法 fibonacci() 的地址仿便,然后跳轉(zhuǎn)到這個地址去執(zhí)行代碼体啰,最后CPU執(zhí)行完方法 fibonacci() 之后,要能夠返回嗽仪。首先找到調(diào)用方法的下一條語句的地址:也就是int[] c=b;的地址荒勇,再跳轉(zhuǎn)到這個地址去執(zhí)行。CPU通過CPU的堆棧寄存器找到調(diào)用方法的參數(shù)和返回地址.
有三個方法A闻坚、B沽翔、C,他們的調(diào)用關(guān)系是A->B->C(A調(diào)用B鲤氢,B調(diào)用C)搀擂,在運行時西潘,會構(gòu)建出下面這樣的調(diào)用棧卷玉。每個方法在調(diào)用棧里都有自己的獨立空間,稱為棧幀喷市,每個棧幀里都有對應(yīng)方法需要的參數(shù)和返回地址相种。當(dāng)調(diào)用方法時,會創(chuàng)建新的棧幀品姓,并壓入調(diào)用棧寝并;當(dāng)方法返回時,對應(yīng)的棧幀就會被自動彈出腹备。也就是說衬潦,棧幀和方法是同生共死的。每個線程都有自己獨立的調(diào)用棧
Java方法里面的局部變量不存在并發(fā)問題植酥,因為每個線程都有自己的調(diào)用棧镀岛,局部變量保存在線程各自的調(diào)用棧里面弦牡,不會共享,所以自然也就沒有并發(fā)問題漂羊。再次重申一遍:沒有共享驾锰,就沒有傷害。
這個思路很好走越,已經(jīng)成為解決并發(fā)問題的一個重要技術(shù)椭豫,同時還有個響當(dāng)當(dāng)?shù)拿纸凶鼍€程封閉,比較官方的解釋是:僅在單線程內(nèi)訪問數(shù)據(jù)
例如:從數(shù)據(jù)庫連接池里獲取的連接Connection旨指,在JDBC規(guī)范里并沒有要求這個Connection必須是線程安全的赏酥。數(shù)據(jù)庫連接池通過線程封閉技術(shù),保證一個Connection一旦被一個線程獲取之后谆构,在這個線程關(guān)閉Connection之前的這段時間里今缚,不會再分配給其他線程,從而保證了Connection不會有并發(fā)問題低淡。
遞歸調(diào)用太深姓言,為什么可能導(dǎo)致棧溢出?
因為每調(diào)用一個方法就會在棧上創(chuàng)建一個棧幀蔗蹋,方法調(diào)用結(jié)束后就會彈出該棧幀何荚,而棧的大小不是無限的,所以遞歸調(diào)用次數(shù)過多的話就會導(dǎo)致棧溢出猪杭。而遞歸調(diào)用的特點是每遞歸一次餐塘,就要創(chuàng)建一個新的棧幀,而且還要保留之前的環(huán)境(棧幀)皂吮,直到遇到結(jié)束條件戒傻。所以遞歸調(diào)用一定要明確好結(jié)束條件,不要出現(xiàn)死循環(huán)蜂筹,而且要避免棧太深需纳。
解決方法:
- 簡單粗暴,不要使用遞歸艺挪,使用循環(huán)替代不翩。缺點:代碼邏輯不夠清晰;
- 限制遞歸次數(shù)麻裳;
- 使用尾遞歸口蝠,尾遞歸是指在方法返回時只調(diào)用自己本身,且不能包含表達(dá)式津坑。編譯器或解釋器會把尾遞歸做優(yōu)化妙蔗,使遞歸方法不論調(diào)用多少次,都只占用一個棧幀疆瑰,所以不會出現(xiàn)棧溢出眉反。然鵝狞谱,Java沒有尾遞歸優(yōu)化