探索線程安全:HashMap 的四種使用技巧

?這篇文章,我們聊聊線程安全使用 HashMap 的四種技巧。

1 方法內部:每個線程使用單獨的 HashMap

如下圖,tomcat 接收到到請求后,依次調用控制器 Controller、服務層 Service 、數據庫訪問層的相關方法。

每次訪問服務層方法 serviceMethod 時,都會在方法體內部創建一個單獨的 HashMap , 將相關請求參數拷貝到 HashMap 里,然后調用 DAO 方法進行數據庫操作。

每個 HTTP 處理線程在服務層方法體內部都有自己的?HashMap?實例,在多線程環境下,不需要對?HashMap?進行任何同步操作。

這也是我們使用最普遍也最安全的的方式,是 CRUD 最基本的操作。

2 配置數據:初始化寫,后續只提供讀

系統啟動之后,我們可以將配置數據加載到本地緩存 HashMap 里 ,這些配置信息初始化之后,就不需要寫入了,后續只提供讀操作。

上圖中顯示一個非常簡單的配置類 SimpleConfig ,內部有一個 HashMap 對象 configMap 。構造函數調用初始化方法,初始化方法內部的邏輯是:將配置數據存儲到 HashMap 中。

SimpleConfig 類對外暴露了 getConfig 方法 ,當 main 線程初始化 SimpleConfig 對象之后,當其他線程調用 getConfig 方法時,因為只有讀,沒有寫操作,所以是線程安全的。

3 讀寫鎖:寫時阻塞,并行讀,讀多寫少場景

讀寫鎖是一把鎖分為兩部分:讀鎖和寫鎖,其中讀鎖允許多個線程同時獲得,而寫鎖則是互斥鎖。

它的規則是:<strong style="font-size: inherit;line-height: inherit;color: rgb (255, 104, 39);"> 讀讀不互斥,讀寫互斥,寫寫互斥 </strong>,適用于讀多寫少的業務場景。

我們一般都使用 ReentrantReadWriteLock ,該類實現了 ReadWriteLock 。ReadWriteLock 接口也很簡單,其內部主要提供了兩個方法,分別返回讀鎖和寫鎖 。

 public interface ReadWriteLock {//獲取讀鎖Lock readLock();//獲取寫鎖Lock writeLock();
}

讀寫鎖的使用方式如下所示:

  1. 創建 ReentrantReadWriteLock 對象,當使用 ReadWriteLock 的時候,并不是直接使用,而是獲得其內部的讀鎖和寫鎖,然后分別調用 lock /unlock 方法;
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  1. 讀取共享數據 ;
Lock readLock = readWriteLock.readLock();
readLock.lock();
try {// TODO 查詢共享數據
} finally {readLock.unlock();
}
  1. 寫入共享數據;
Lock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {// TODO 修改共享數據
} finally {writeLock.unlock();
}

下面的代碼展示如何使用 ReadWriteLock 線程安全的使用 HashMap :

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockCache {// 創建一個 HashMap 來存儲緩存的數據private Map<String, String> map = new HashMap<>();// 創建讀寫鎖對象private ReadWriteLock rw = new ReentrantReadWriteLock();// 放對象方法:向緩存中添加一個鍵值對public void put(String key, String value) {// 獲取寫鎖,以確保當前操作是獨占的rw.writeLock().lock();try {// 執行寫操作,將鍵值對放入 mapmap.put(key, value);} finally {// 釋放寫鎖rw.writeLock().unlock();}}// 取對象方法:從緩存中獲取一個值public String get(String key) {// 獲取讀鎖,允許并發讀操作rw.readLock().lock();try {// 執行讀操作,從 map 中獲取值return map.get(key);} finally {// 釋放讀鎖rw.readLock().unlock();}}
}

使用讀寫鎖操作 HashMap 是一個非常經典的技巧,消息中間件 RockeMQ NameServer (名字服務)保存和查詢路由信息都是通過這種技巧實現的。

另外,讀寫鎖可以操作多個 HashMap ,相比 ConcurrentHashMap 而言,ReadWriteLock 可以控制緩存對象的顆粒度,具備更大的靈活性。

4 Collections.synchronizedMap : 讀寫均加鎖

如下代碼,當我們多線程使用 userMap 時,

static Map<Long, User> userMap = Collections.synchronizedMap(new HashMap<Long, User>());

進入 synchronizedMap 方法:

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {return new SynchronizedMap<>(m);
}

SynchronizedMap 內部包含一個對象鎖 Object mutex ,它本質上是一個包裝類,將 HashMap 的讀寫操作重新實現了一次,我們看到每次讀寫時,都會用 synchronized 關鍵字來保證操作的線程安全。

雖然 Collections.synchronizedMap 這種技巧使用起來非常簡單,但是我們需要理解它的每次讀寫都會加鎖,性能并不會特別好。

5 總結

這篇文章,筆者總結了四種線程安全的使用 HashMap 的技巧。

1、方法內部:每個線程使用單獨的 HashMap

這是我們使用最普遍,也是非常可靠的方式。每個線程在方法體內部創建?HashMap?實例,在多線程環境下,不需要對?HashMap?進行任何同步操作。

2、 配置數據:初始化寫,后續只提供讀

中間件在啟動時,會讀取配置文件,將配置數據寫入到 HashMap 中,主線程寫完之后,以后不會再有寫入操作,其他的線程可以讀取,不會產生線程安全問題。

3、讀寫鎖:寫時阻塞,并行讀,讀多寫少場景

讀寫鎖是一把鎖分為兩部分:讀鎖和寫鎖,其中讀鎖允許多個線程同時獲得,而寫鎖則是互斥鎖。

它的規則是:<strong style="font-size: inherit;line-height: inherit;color: rgb (255, 104, 39);"> 讀讀不互斥,讀寫互斥,寫寫互斥 </strong>,適用于讀多寫少的業務場景。

使用讀寫鎖操作 HashMap 是一個非常經典的技巧,消息中間件 RockeMQ NameServer (名字服務)保存和查詢路由信息都是通過這種技巧實現的。

4、Collections.synchronizedMap : 讀寫均加鎖

Collections.synchronizedMap 方法使用了裝飾器模式為線程不安全的 HashMap 提供了一個線程安全的裝飾器類 SynchronizedMap。

通過 SynchronizedMap 來間接的保證對 HashMap 的操作是線程安全,而 SynchronizedMap 底層也是通過 synchronized 關鍵字來保證操作的線程安全。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/40333.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/40333.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/40333.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue H5頁面video 視頻流自動播放, 解決ios不能自動播放問題

視頻組件 <videostyle"width: 100%; height: 100%;object-fit: fill"class"player"refplayer_big_boxcontrolspreloadautoplay //自動播放muted //是否靜音playsinline"true"x5-playsinline""webkit-playsinline"tru…

[Linux安全運維] Linux用戶以及權限管理

Linux用戶以及權限管理 Linux用戶和組 用戶信息文件pasawd /etc/passwd文件用于存儲用戶的信息 :用于分割不同的字段信息 字段示例&#xff08;第一行&#xff09;含義說明1root用戶名2x密碼占位符x代表用戶有密碼存儲在shadow文件中無內容代表用戶登錄系統不需要密碼30UID…

梧桐數據庫:存算分離和存算一體架構的分布式數據庫技術分析

摘要&#xff1a; 隨著數據量的不斷增長和對數據處理性能的要求越來越高&#xff0c;分布式數據庫技術成為了數據存儲和處理的重要解決方案。存算分離和存算一體是兩種常見的分布式數據庫架構&#xff0c;它們在數據存儲和計算方面有著不同的特點和優勢。本文將對存算分離和存算…

Spring源碼(一) 如何閱讀 Spring 源碼

學習 Spring 的源碼&#xff0c;也可以通過 SpringBoot 搭環境。 不管是什么源碼&#xff0c;最好寫個 demo&#xff0c;跑起來&#xff0c;然后從常用的類和方法入手&#xff0c;跟蹤調試。 配置對象 新建一個 SpringBoot 的項目&#xff0c; 詳情見&#xff1a; https://b…

FreeRTOS 中 vListInsertEnd 函數詳解

在 FreeRTOS 中&#xff0c;vListInsertEnd 函數用于將新項插入到指定列表的尾部&#xff08;但實際行為是插入到一個特定的索引位置之前&#xff09;。FreeRTOS 使用雙向鏈表&#xff08;doubly linked list&#xff09;來管理任務和其他系統對象&#xff0c;這樣可以高效地插…

前端三件套開發模版——產品介紹頁面

今天有空&#xff0c;使用前端三件套html、css、js制作了一個非常簡單的產品制作頁面&#xff0c;與大家分享&#xff0c;希望可以滿足大家應急的需求。本頁面可以對產品進行“搶購”、對產品進行介紹&#xff0c;同時可以安排一張產品的高清大圖&#xff0c;我也加入了頁面的背…

JAVA實現二分查找,斐波那契數列,深度優先搜索詳情教程【包含代碼】

本人詳解 作者:王文峰,參加過 CSDN 2020年度博客之星,《Java王大師王天師》 公眾號:JAVA開發王大師,專注于天道酬勤的 Java 開發問題中國國學、傳統文化和代碼愛好者的程序人生,期待你的關注和支持!本人外號:神秘小峯 山峯 轉載說明:務必注明來源(注明:作者:王文峰…

react+ts+antd項目搭建

前言&#xff1a; 基于ts語言創建react項目&#xff0c;node版本是v16.14.2 一、 腳手架創建項目 全局安裝 npm install -g creacte-react-app創建項目file-management&#xff0c;ts需要添加–template typescript npx create-react-app file-management --template typesc…

Ubuntu查看opencv版本c++

?命令行中直接輸入&#xff1a; pkg-config --modversion opencv?命令行中直接輸入&#xff1a; pkg-config --modversion opencv4注解&#xff1a;附上在markdown中打勾&#xff0c;對號和打叉。使用時將&和#之間的空格去掉&#xff0c;這里只是為了不讓CSDN自動轉換才…

Ubuntu20.04 c++程序 涉及opencv問題記錄

頭文件更改 默認的頭文件引用是 #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp>但是在ubuntu20中/usr/include中默認的是opencv4&#xff0c;他文件夾里面才是opencv2&#xff0c;需要…

vue3單個頁面進行防抖節流

防抖 <template><button id"submitButton" ref"submitButton">GET</button> </template><script lang"ts" setup> import { ref, onMounted } from vue;// 防抖函數 function debounce(func: () > void, dela…

【mybatis】mybatis-plus中Wrapper(查詢條件構造器)簡介_常用方法

1、簡介 MyBatis-Plus 是一個 MyBatis 的增強工具&#xff0c;在 MyBatis 的基礎上只做增強不做改變&#xff0c;為簡化開發、提高效率而生。MyBatis-Plus 提供了強大的條件構造器&#xff08;Wrapper&#xff09;&#xff0c;用于構建復雜的 SQL 查詢條件&#xff0c;使得我們…

溝通方法和技巧

0 Preface/Foreword 1 溝通對象 溝通維度&#xff1a; upward&#xff0c;向上溝通&#xff0c;直接上級downward&#xff0c;向下溝通&#xff0c;直接下級horizontal&#xff0c;橫向溝通&#xff0c;同部門/跨部門同事 2 溝通方式&#xff08;5W2H&#xff09; 對于開會和…

小白嘗試某程機票信息爬取

實訓課需要機票數據集&#xff0c;網上沒有&#xff0c;所以我選擇爬取數據 此過程可謂經歷的九九八十一難&#xff0c;也參考了不少大佬的文章&#xff0c;在此特別記錄一下 彎路不多說&#xff0c;我直接講成功的方法 找到請求url 通過控制臺&#xff0c;最后確認下面的 …

在WordPress中獲取10天之內的文章更新數

要在WordPress中獲取10天之內的文章更新數&#xff0c;您可以使用以下代碼片段。這段代碼將顯示在過去10天內更新的文章數量。 <?php // 獲取當前時間戳 $now time();// 計算10天前的時間戳 $ten_days_ago $now - (10 * 24 * 60 * 60);// 設置查詢參數 $args array(pos…

【Spring Boot AOP中切入表達式格式介紹】

文章目錄 一、切入表達式簡介二、切入表達式的語法1. 方法匹配符示例&#xff1a; 2. 類型匹配符示例&#xff1a; 一、切入表達式簡介 切入表達式&#xff08;Pointcut Expression&#xff09;是AOP中定義切入點&#xff08;Pointcut&#xff09;的一種方式。它定義了在哪些連…

基于Java中的SSM框架實現物流管理系統項目【項目源碼+論文說明】

基于Java中的SSM框架實現物流管理系統演示 摘要 企業的發展離不開物流的運輸&#xff0c;在一個大型的企業中&#xff0c;商品的生產和建設&#xff0c;推廣只是前期的一些工作&#xff0c;在后期的商品銷售和物流方面的建立&#xff0c;才能讓一個企業得到大力的發展。 企業…

基于Linux/ARM/單片機利用狀態機對多個按鍵進行掃描實現短按或者長按

1&#xff09;Linux/ARM/單片機入門級按鍵掃描程序設計&#xff0c;分享給將要學習或者正在學習Linux/ARM/單片機開發的同學。 2&#xff09;內容屬于原創&#xff0c;若轉載&#xff0c;請說明出處。 3&#xff09;提供相關問題有償答疑和支持。 學習Linux/ARM/單片機的同學…

docker部署簡單的Kafka

文章目錄 1. 拉取鏡像2. 運行創建網絡運行 ZooKeeper 容器運行 Kafka 容器 3. 簡單的校驗1. 檢查容器狀態2. 檢查 ZooKeeper 日志3. 檢查 Kafka 日志4. 使用 Kafka 命令行工具檢查5. 創建和刪除測試主題 1. 拉取鏡像 選擇一組兼容性好的版本。 docker pull bitnami/kafka:3.6…

Facebook:數字社交的引領者與創新者

自2004年誕生以來&#xff0c;Facebook從一個校園網絡項目迅速成長為全球最大的社交媒體平臺&#xff0c;徹底改變了我們與世界互動的方式。作為數字社交的引領者和創新者&#xff0c;Facebook不僅在技術層面上不斷突破&#xff0c;也在社會和文化領域留下了深刻的印記。本文將…