這些併發容器的坑,你要謹記!

来源:https://www.cnblogs.com/huaweiyun/archive/2022/08/29/16636539.html
-Advertisement-
Play Games

摘要:JDK1.5及之後的版本中,提供的線程安全的容器,一般被稱為併發容器。與同步容器一樣,併發容器在總體上也可以分為四大類,分別為:List、Set、Map和Queue。 本文分享自華為雲社區《【高併發】要想學好併發編程,這些併發容器的坑是你必須要註意的!!(建議收藏)》,作者:冰 河 。 其實, ...


摘要:JDK1.5及之後的版本中,提供的線程安全的容器,一般被稱為併發容器。與同步容器一樣,併發容器在總體上也可以分為四大類,分別為:List、Set、Map和Queue。

本文分享自華為雲社區《【高併發】要想學好併發編程,這些併發容器的坑是你必須要註意的!!(建議收藏)》,作者:冰 河 。

其實,在JDK1.5之前的線程安全的容器,大多數都是指同步容器,使用同步容器進行併發編程時,最大的問題就是性能很差。因為同步容器中的所有方法都是使用synchronized鎖進行互斥,串列度太高了,無法真正的做到並行。

所以,在JDK1.5之後,JDK中提供了併發性能更好的容器。JDK1.5及之後的版本中,提供的線程安全的容器,一般被稱為併發容器。

併發容器

與同步容器一樣,併發容器在總體上也可以分為四大類,分別為:List、Set、Map和Queue。總體上如下圖所示。

接下來,我們分別介紹下這些併發容器在使用時的註意事項和避免踩到的坑。

List

併發容器中的List相對來說比較簡單,就一個CopyOnWriteArrayList。大家可以從字面的意思中就能夠體會到:CopyOnWrite,在寫的時候進行複製操作,也就是說在進行寫操作時,會將共用變數複製一份。那這樣做有什麼好處呢?最大的好處就是:讀操作可以做到完全無鎖化。

在CopyOnWriteArrayList內部維護了一個數組,成員變數array指向這個數組,其核心源代碼如下所示。

private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}

當進行操作時,都是基於array指向的這個內部數組進行的。例如,我們使用Iterator迭代器遍歷這個數組時,會按照下圖所示的方式進行讀操作。

如果在遍歷CopyOnWriteArrayList時發生寫操作,例如,向數組中增加一個元素時,CopyOnWriteArrayList則會將內部的數組複製一份出來,然後會在新複製出來的數組上添加新的元素,添加完再將array指向新的數組,如下圖所示。

對於CopyOnWriteArrayList的其他寫操作和添加元素的操作原理相同,這裡就不再贅述了。

使用CopyOnWriteArrayList時需要註意的是:

  • CopyOnWriteArrayList只適合寫操作比較少的場景,並且能夠容忍讀寫操作在短時間內的不一致。
  • CopyOnWriteArrayList的迭代器是只讀的,不支持寫操作。

Set

對於Set介面來說,併發容器中主要有兩個實現類,一個是CopyOnWriteArraySet,另一個是ConcurrentSkipListSet。其中,CopyOnWriteArraySet的使用場景、原理與註意事項和CopyOnWriteArrayList一致。而ConcurrentSkipListSet的使用場景、原理和註意事項和下文的ConcurrentSkipListMap一致。這裡,我就不再贅述啦。

Map

在併發容器中,Map介面的實現類主要有ConcurrentHashMap和ConcurrentSkipListMap,而ConcurrentHashMap和ConcurrentSkipListMap最大的區別就是:ConcurrentHashMap的Key是無序的,而ConcurrentSkipListMap的Key是有序的。

在使用ConcurrentHashMap和ConcurrentSkipListMap時,需要註意的是:ConcurrentHashMap和ConcurrentSkipListMap的Key和Value都不能為空。

這裡,我們可以將Map相關的類總結成一個表格,如下所示。

這樣,大家記憶起來就方便多了。

這裡,ConcurrentSkipListMap是基於“跳錶”實現的,跳錶的插入、刪除、查詢的平均時間複雜度為O(log n),這些時間複雜度在理論上與線程數沒有關係。如果要追求性能的話,可以嘗試使用ConcurrentSkipListMap。

Queue

在Java的併發容器中,Queue相對來說比較複雜。我們先來瞭解幾個概念:

  • 阻塞隊列:阻塞一般就是指當隊列已滿時,入隊操作會阻塞;當隊列為空時,出隊操作就會阻塞。
  • 非阻塞隊列:隊列的入隊和出隊操作不會阻塞。
  • 單端隊列:隊列的入隊操作只能在隊尾進行,隊列的出隊操作只能在隊首進行。
  • 雙端隊列:隊列的入隊操作和出隊操作都可以在隊首和隊尾進行。

我們可以將上述的隊列進行組合,將隊列分為單端阻塞隊列、雙端阻塞隊列、單端非阻塞隊列和雙端非阻塞隊列。

在Java的併發容器中,會使用明顯的標識來區分不同類型的隊列。

  • 阻塞隊列一個明顯的標識就是使用Blocking修飾,例如,ArrayBlockingQueue和LinkedBlockingQueue都是阻塞隊列。
  • 單端隊列會使用Queue標識,例如ArrayBlockingQueue和LinkedBlockingQueue也是單端隊列。
  • 雙端隊列會使用Deque標識,例如LinkedBlockingDeque和ConcurrentLinkedDeque都是雙端隊列。

接下來,我們就分別簡單聊聊這四種類型的隊列。

單端阻塞隊列

在Java的併發容器中,單端阻塞隊列的主要實現是BlockingQueue,主要包括:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、LinkedTransferQueue、PriorityBlockingQueue和DelayQueue。

單端阻塞隊列的內部一般會有一個隊列。

在實現上,內部的隊列可以是數組,例如ArrayBlockingQueue,也可以是鏈表,例如LinkedBlockingQueue。

也可以在內部不存在隊列,例如SynchronousQueue,SynchronousQueue實現了生產者的入隊操作必須等待消費者的出隊操作完成之後才能進行。

LinkedTransferQueue集成了LinkedBlockingQueue和SynchronousQueue的優點,並且性能比LinkedBlockingQueue好。

PriorityBlockingQueue實現了按照優先順序進行出隊操作,也就是說,隊列元素在PriorityBlockingQueue內部可以按照某種規則進行排序。

DelayQueue是延時隊列,實現了在一段時間後再出隊的操作。

雙端阻塞隊列

雙端阻塞隊列的實現主要是LinkedBlockingDeque。示意圖如下所示。

單端非阻塞隊列

單端非阻塞隊列的實現主要是ConcurrentLinkedQueue,示意圖如下所示。

雙端非阻塞隊列

雙端非阻塞隊列的實現主要是ConcurrentLinkedDeque,示意圖如下所示。

有界與無界隊列

使用隊列時,還要註意隊列的有界與無界問題,也就是在使用隊列時,需要註意隊列是否有容量限制。

在實際工作中,一般推薦使用有界隊列。因為無界隊列很容易導致記憶體溢出的問題。在Java的併發容器中,只有ArrayBlockingQueue和LinkedBlockingQueue支持有界,其他的隊列都是無界隊列。

在使用時,一定要註意記憶體溢出問題。

總結

今天我們主要介紹了JDK1.5之後提供的併發容器,主要包括:List、Set、Map和Queue,而Queue又可以分為:單端阻塞隊列、雙端阻塞隊列、單端非阻塞隊列和雙端非阻塞隊列。對於每種併發容器,我們簡單介紹了其基本原理和註意事項。

 

點擊關註,第一時間瞭解華為雲新鮮技術~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄 一.OpenGL 伽馬線 1.IOS Object-C 版本 2.Windows OpenGL ES 版本 3.Windows OpenGL 版本 二.OpenGL 伽馬線 GLSL Shader 三.猜你喜歡 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> ...
  • JVM執行引擎的作用就是將位元組碼指令解釋或者編譯為對應平臺上的本地機器指令。簡單來說,執行引擎充當了將高級語言翻譯為機器語言的翻譯者。 ...
  • 《看漫畫學Python:有趣、有料、好玩、好用:全彩版》PDF高清版免費下載地址 內容簡介 · · · · · · Python是一門既簡單又強大的編程語言,被廣泛應用於數據分析、大數據、網路爬蟲、自動化運維、科學計算和人工智慧等領域。Python也越來越重要,成為國家電腦等級考試科目,某些中小學 ...
  • Java泛型01 1.泛型的理解和好處 看一個需求: 請編寫程式,在ArrayList中添加三個Dog對象 Dog對象含有name和age,並輸出name和age(要求使用getXXX()) 先用傳統的方法來解決 >引出泛型 傳統的方法: package li.generic; import jav ...
  • 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 特效 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> O ...
  • 軟體架構 1、C/S架構:客戶端 / 伺服器 QQ,Typora,騰訊會議。 2、B/S架構:瀏覽器 / 伺服器 京東,愛奇藝,B站。 資源分類 靜態資源:所有用戶訪問後,得到的結果都是一樣的。(HTML,CSS,JS,圖片,音頻,視頻...) 動態資源:每個用戶訪問相同的資源,得到的結果可能不一樣 ...
  • 據說,Rust語言語法的高門檻是勸退很多人上手的主要原因。 確實,Rust語言希望解決 C/C++ 手工管理記憶體的問題,但是又不想引入類似golang,java的GC機制。 因此,為了能讓編譯器能夠在編譯階段檢查出潛在的記憶體問題,Rust的語法上就多了一些其他語言所沒有的規則,這些規則讓上手Rust ...
  • “Http協議和RPC協議有什麼區別?” 最近很多人問我這個問題,他們都不知道怎麼回答。 今天我們就來瞭解一下這個問題的高手回答。 另外,我把文字版本的內容整理到了一個15W字的面試文檔里了。 大家可以看文章尾端領取。 下麵看看高手的回答 高手: 這個問題我想從三個層面來回答。 從功能特性來說。 h ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...