亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java多線程中易犯的錯誤是什么

發布時間:2022-01-06 15:23:05 來源:億速云 閱讀:109 作者:iii 欄目:大數據

本篇內容介紹了“java多線程中易犯的錯誤是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

一、什么時候應該使用多線程?

不知道大家有沒有想過這個問題,就是什么時候我該使用多線程呢?使用多線程就一定會提升系統性能嗎?

1、其實是否應該使用多線程在很大程度上取決于應用程序的類型。

  • 計算密集型(如純數學運算) 的, 并受CPU 功能的制約, 則只有多CPU(或者多個內核) 機器能夠從更多的線程中受益, 單CPU下, 多線程不會帶來任何性能上的提升, 反而有可能由于線程切換等額外開銷而導致性能下降

  • IO密集型的,當我的應用必須等待緩慢的資源(如網絡連接或者數據庫連接上返回數據)時,那么多線程會讓系統的CPU充分的利用起來,當一個線程阻塞掛起時,另一個線程可以繼續使用CPU資源。

  • 其實,就是多線程不會增加CPU的處理能,而是能夠更加充分地利用CPU資源。

由于同一進程的多個線程是共享同一片內存資源的,在帶來方便的同時也必然會增加其復雜性,如何保證多線程訪問數據的一致性問題等。而多線程屬于編程中容易翻車的地方。并且多線程編程問題的測試定位也是比較難的。總體來說,好的多線程是寫出來,將多線程問題寄希望于測試中發現, 無疑是極度不可靠的。SO,努力的學習吧。

Java API 與多線程息息相關的的幾大關鍵字:volatile、synchronized、 wait、 notify. 理解了這幾個關鍵字,就可以編寫多線程的代碼了。

二、什么時候需要加鎖?

在多線程場合下,最重要的就是保障數據的一致性問題,而保障數據一致性問題,就需要借助于鎖了。

其實我們在多線程的場景下應該搞清楚一個問題,就是到底什么需要保護?并不是所有的的數據都需要加鎖保護,只有那些涉及到被多線程訪問的共享的數據才需要加鎖保護。

鎖的本質其實就是確保在同一時刻,只有一個線程在訪問共享數據,那么此時該共享數據就能得到有效的保護。

舉例說明下,比如我們想構造一個多線程下安全的單向鏈表:

<img src="/Users/luqiang/Downloads/公眾號圖片/鏈表插入新圖.jpg" alt="鏈表插入新圖"  />

假如現在有兩個線程在操作這個鏈表,一個寫線程插入一個新元素7,另一個讀線程遍歷鏈表數據,如果不使用任何鎖,那就有可能出現下面的執行順序:

步驟寫線程讀線程
0修改鏈表元素2的next指針指向7這個元素... ...
1... ...遍歷鏈表,到7的時候發現next 為null,遍歷結束
2修改元素7的next 指針指向3... ...

通過上面的例子我們可以明顯看到在多線程下操作這個鏈表,有可能會導致讀線程讀到的數據不完整,只有從鏈表頭部到元素7的位置的數據。由此可見,不加入任何保護措施的多線程保護,勢必會導致數據的混亂。為了避免數據一致性問題,我們就需要將操作該隊列的代碼放入同步塊內(鎖的對象也就是這個鏈表實例),來確保同一時刻只有一個線程可以訪問該鏈表。

如何加鎖?

這里簡單的說下,一般我們都是使用synchronized(如果沒有特殊需求建議直接使用這個關鍵字,jdk新版本它真的很快),記住synchronized 鎖的就是對象頭。

簡單的說下,主要有下面幾種用法:

  • synchronized 放在方法上,鎖的是當前synchronized 方法的對象實例

  • synchronized在static 方法上,鎖的是synchronized 方法的class 類對象,注意這里class 其實也是一個對象。

  • synchronized(this)在代碼塊中,鎖的是代碼塊括號內的對象,這里this指的就是調用這個方法的類實例對象

三、 多線程中易犯的錯誤

1、鎖范圍過大

共享資源訪問完成后, 后續的代碼沒有放在synchronized同步代碼塊之外。 會導致當前線程長期無效的占用該鎖, 而其它爭用該鎖的線程只能等待, 最終導致性能受到極大影響。

 public void test()
 {
 		synchronized(lock){
 		... ... //正在訪問共享資源
 		... ... //做其它耗時操作,但這些耗時操作與共享資源無關
 	}
 }

面對上面這種寫法,會導致此線程長期占有此鎖,從而導致其他線程只能等待,下面來討論下解決方法:

1)單CPU場景下,將不需要同步的耗時操作拿到同步塊外面,有的情況可以提升性能,有的卻不行。

  • CPU密集型的代碼 ,不存在磁盤IO/網絡IO等低CPU消耗的代碼。 這種情況下, CPU 99%都在執行代碼。 因此縮小同步塊也不會帶來任何性能上的提升, 同時縮小同步塊也不會帶來性能上的下降。

  • IO密集型的代碼,在執行不消耗CPU的代碼時,其實CPU屬于空閑狀態的。如果此時讓CPU工作起來就可以帶來整體上性能的提升。所以在這種情況下,就可以將不需要同步的耗時操作移到同步塊外面了。

2)多CPU場景下,將耗時的CPU操作拿到同步塊外面,總是可以提升性能的

  • CPU密集型的代碼,不存在IO操作等不消耗CPU的代碼片段。因為當前是多CPU,其他CPU也可能是空閑的。所以在縮小同步塊的時候,也會讓其他線程盡快的執行這段代碼從而帶來性能上的提升。

  • IO密集型的代碼,因為當前PCU都是空閑的狀態,所以將耗時的操作放在同步塊外面,一定會帶來整體上的性能提升。

當然,不管怎么樣,縮小鎖的同步范圍對于系統來說都是百利而無一害的,因此上面的代碼應該改為:

 public void test()
 {
 		synchronized(lock){
 		... ... //正在訪問共享資源
 	}
 		... ... //做其它耗時操作,但這些耗時操作與共享資源無關
 }

綜上所述,一個重點,就是只將訪問共享資源的代碼放在同步塊內,保證快進快出。

2、死鎖的問題

死鎖要知道的:

  • 死鎖,簡單地說就是兩個線程或多個線程在同時等待被對方持有的鎖導致的,死鎖會導致線程無法繼續執行并被永久掛起。

  • 如果線程發生了死鎖,那我們就能從線程堆棧中明顯的看到”Found one Java-level deadlock“,并且線程棧還會給出死鎖的分析結果。

  • 死鎖這種問題如果發生在關鍵系統上就可能會導致系統癱瘓,如果想要快速恢復系統,臨時唯一的方法就是保留線程棧先重啟,然后再盡快的恢復。

  • 死鎖這種問題有時候測試是很難被立即發現的,很多時候在測試時能否及時發現這類問題,就全看你的運氣和你準備的測試用例了。

  • 避免死鎖這類問題,唯一的辦法就是改代碼。但一個可靠的系統是設計出來的,而不是通過改BUG改出來的,當出現這種問題的時候就需要從系統設計角度去分析了。

  • 有人會認為死鎖會導致CPU 100%,其實對也不對。 要看使用的什么類型的鎖了,比如synchronized導致的死鎖,那就不會導致CPU100%,只會掛起線程。但如果是自旋鎖這種才可能會消耗CPU。

3、共用一把鎖的問題

就是多個共享變量會共用一把鎖,特別是在方法級別上使用synchronized,從而人為導致的鎖競爭。

上例子,下面是新手容易犯的錯誤:

1 public class MyTest
2 {
3 Object shared;
4 synchronized void fun1() {...} //訪問共享變量shared
5 synchronized void fun2() {...} //訪問共享變量shared
6 synchronized void fun3() {...} //不訪問共享變量shared
7 synchronized void fun4() {...} //不訪問共享變量shared
8 synchronized void fun5() {...} //不訪問共享變量shared
9 }

上面的代碼每一個方法都被加了synchronized ,明顯違背了保護什么鎖什么的原則。

三、線程數我們一般設多少比較合理呢?

其實大家都知道,在大多數場合下多線程都是可以提高系統的性能和吞吐量,但一個系統到底多少個線程才是合理的?

總的來說,線程數量太多太少其實都不太好,多了會因為線程頻繁切換導致開銷增大,有時候反而降低了系統性能。少了又會導致CPU資源不能充分的利用起來,性能沒有達到瓶頸。

所以,系統到底使用多少線程合適,是要看系統的線程是否能充分的利用了CPU。其實實際情況,是很多時候不消耗CPU,如:磁盤IO、網絡IO等。

磁盤IO、網絡IO相比CPU的速度,那簡直是相當的慢的,在執行IO的這段時間里CPU其實是空閑的。如果這時其他線程能把這空閑的CPU利用上,就可以達到提示系統性能和吞吐的目的。

其實上面我們也提到過,也就是兩種計算特性:

CPU密集型: 因為每個CPU都是高計算負載的情況,如果設置過多的線程反而會產生不必要的上下文切換。所以,一般線程我們會設置 CPU 核數 + 1就可以了,為啥要加1 呢,即使當計算(CPU)密集型的線程偶爾由于頁缺失故障或者其他原因而暫停時,這個“額外”的線程也能確保 CPU 的時鐘周期不會被浪費,其實就是個備份。

IO密集型:因為大量的IO操作,會導致CPU處于空閑狀態,所以這時我們可以多設置些線程。 所以, 線程數 = CPU 核心數 * (1+ IO 耗時/CPU 耗時) 就可以了,希望能給你點啟發。

“java多線程中易犯的錯誤是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

游戏| 华容县| 崇信县| 乾安县| 穆棱市| 遵化市| 千阳县| 双桥区| 临海市| 富民县| 吉林市| 天门市| 贵州省| 长治市| 宁阳县| 西峡县| 镇康县| 唐海县| 临江市| 五莲县| 阜宁县| 苏州市| 辉县市| 宝清县| 湖州市| 清苑县| 前郭尔| 吉木乃县| 靖远县| 巴马| 乐业县| 新沂市| 保亭| 钟祥市| 高清| 砚山县| 河津市| 思茅市| 静乐县| 石棉县| 南昌市|