CAS 一些隱藏的知識,您了解嗎

目錄

ConcurrentHashMap 一定是線程安全的嗎

CAS 機制的注意事項

使用java 并行流 ,您要留意了


??????ConcurrentHashMap 在JDK1.8中ConcurrentHashMap 內部使用的是數組加鏈表加紅黑樹的結構,通過CAS+volatile或synchronized的方式來保證線程安全的,這些原理已毋庸置疑,一言不合上代碼.

? ? ?1.? 模擬2個線程累計,通過ConcurrentHashMap 儲存累計的結果。


/*** @description: ConcurrentHashMap 真的安全嗎* @author: ppx* @date: 2023/8/17 14:11* @version: 1.0*/
public class TestMap {private static ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();private static String key = "hello";/*** @description: 測試2個線程 執行計算* @param:* @return: void* @author: ppx* @date: 2023/8/17 16:43*/private static void testRun() {ExecutorService executor = new ThreadPoolExecutor(2, 5,2L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());for (int i = 0; i < 2; i++) {executor.submit(() -> {for (int j = 0; j < 5; j++) {// 第一步讀取int value = concurrentHashMap.getOrDefault(key, 0);// 第二步+1value++;// 第三補+ 回寫mapconcurrentHashMap.put(key, value);}});}executor.shutdown();// 直到線程執行完成while(!executor.isTerminated()){}System.out.println("執行結果:" + concurrentHashMap.get(key));}public static void main(String[] args) {testRun();}
}

2.出乎意料執行多次輸出不同的結果

?

?

3. 分析原理:ConcurrentHashMap 本身是線程安全的,但for 里面的獲map取值、加加操作及回寫map 這三步是非原子性。要保證操作的安全性,這三步實現原子性即可。

?優化后代碼

 private static void testRun() {ExecutorService executor = new ThreadPoolExecutor(2, 5,2L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());for (int i = 0; i < 2; i++) {executor.submit(() -> {for (int j = 0; j < 5; j++) {synchronized (TestMap.class) {int value = concurrentHashMap.getOrDefault(key, 0);value ++;concurrentHashMap.put(key, value);}}});}executor.shutdown();while (!executor.isTerminated()) {}System.out.println("執行結果:" + concurrentHashMap.get(key));}

CAS 機制的注意事項

? ? ? ?某線程把數據A更新了B,隨后又從B更新成A,恰好此時另一線程讀取該數據,發現數據的值還是A沒有變化,誤認為還是原來的A,但此時A的一些屬性或狀態已經發生過變化。

? ? ? CAS操作中將判斷“V的值是否仍然為A?”,如果是的話將執行更新操作,在某些CAS操作中,如果V的值首先由A變為B,再由B變為A,那么CAS仍然將會操作成功。

ABA問題

? ????????線程A 的操作,cas中的值由1變成99,再由99變成1,此次線程B 發現AtomicInteger 的值還是1,于是更新到50,產生ABA的問題。

private static AtomicInteger atomicInteger = new AtomicInteger(1);public static void main(String[] args) {Thread threadA  =  new Thread(() -> {atomicInteger.compareAndSet(1, 99);atomicInteger.compareAndSet(99, 1);System.out.println("線程A進行CAS后的值:"+atomicInteger.get());try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}, "線程A");Thread threadB  =  new Thread(() -> {try{atomicInteger.compareAndSet(1, 50);System.out.println("線程B進行CAS后的值:"+atomicInteger.get());}catch (Exception e) {e.printStackTrace();}}, "線程B");threadA.start();try {threadA.join();} catch (InterruptedException e) {e.printStackTrace();}threadB.start();}

基于AtomicStampedReference類實現
AtomicStampedReference內部增加了版本號的概念,只有期待的值與版本號分別匹配后,才滿足條件,更新最新的值。

案例

? ? ? 線程 A? 進行CAS 操作更新時,發布版本已發生變動,CAS更新?失敗。線程B? ?進行CAS 操作更新時,匹配對應的版本,期待值,更新成功。

 public static void main(String[] args) {new Thread(() -> {// 讓線程B 獲取最新版本號,成功 執行更新try {Thread.sleep(11);} catch (InterruptedException e) {e.printStackTrace();}int stamp = atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() + ", 當前版本號為:" + stamp);boolean firstCasFlag = atomicStampedReference.compareAndSet(100, 99, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);System.out.println("當前版本號:"+atomicStampedReference.getStamp()+", 線程A進行CAS后的值:" + atomicInteger.get() + ",第1次操作是否修改成功: " + firstCasFlag);}, "線程A").start();new Thread(() -> {try {int stamp = atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() + ", 版本號為:" + atomicStampedReference.getStamp());boolean flag = atomicStampedReference.compareAndSet(100, 888, stamp, atomicStampedReference.getStamp() + 1);System.out.println("線程B進行CAS后的值:" + atomicStampedReference.getReference() + ", 此次操作是否修改成功: " + flag);} catch (Exception e) {e.printStackTrace();}}, "線程B").start();}

執行結果

線程B, 版本號為:1
線程B進行CAS后的值:888, 此次操作是否修改成功: true
線程A, 當前版本號為:2
當前版本號:2, 線程A進行CAS后的值:1,第1次操作是否修改成功: false
?

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

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

相關文章

TikTok或將于8月底關閉半閉環、速賣通或將推出“半托管”模式

《出海周報》是運營壇為外貿企業主和外貿人獨家打造的重要資訊欄目&#xff0c;聚焦企業出海、海外市場動態、海外監管政策等方面&#xff0c;以簡捷的方式&#xff0c;提升讀者獲取資訊的效率。 接下來運營壇為大家帶來第15期出海周報&#xff0c;快來看看這周國內外市場發生了…

C++--紅黑樹

1.什么是紅黑樹 紅黑樹&#xff0c;是一種二叉搜索樹&#xff0c;但在每個結點上增加一個存儲位表示結點的顏色&#xff0c;可以是Red或Black。 通過對任何一條從根到葉子的路徑上各個結點著色方式的限制&#xff0c;紅黑樹確保沒有一條路徑會比其他路徑長出倆倍&#xff0c;因…

jdbc 使用rewriteBatchedStatements=true后,報錯

jdbc 使用rewriteBatchedStatementstrue后&#xff0c;報錯了 rewriteBatchedStatementstrue解釋 rewriteBatchedStatementstrue是一個配置選項&#xff0c;它影響MySQL JDBC驅動程序的行為。JDBC是Java數據庫連接的標準。當你使用Java程序連接MySQL數據庫時&#xff0c;你需要…

秋招面經——結合各方面試經驗

Mysql mysql事務 共享鎖與排他鎖 共享鎖&#xff1a;允許一個事務去讀一行&#xff0c;阻止其他事務獲得相同數據集的排他鎖。&#xff08;讀都允許讀&#xff0c;但我在讀不允許你去改&#xff09; 排他鎖&#xff1a;允許一個事務去讀一行&#xff0c;阻止其他事務獲得相同…

UVA 10006 埃氏篩法+快速冪

本題目使用費馬定理時&#xff0c;我隨機定義了10個數字&#xff0c;循環用費馬小定理判斷&#xff0c;數組中的值不用和我的相同&#xff0c;隨機即可。 #include <iostream> using namespace std; typedef unsigned long long ll; bool isPrime[65007]; ll a[10]; voi…

【Unity細節】Unity中的層級LayerMask

&#x1f468;?&#x1f4bb;個人主頁&#xff1a;元宇宙-秩沅 hallo 歡迎 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! 本文由 秩沅 原創 &#x1f636;?&#x1f32b;?收錄于專欄&#xff1a;unity細節和bug &#x1f636;?&#x1f32b;?優質專欄 ?【…

Mybatis Plus中使用LambdaQueryWrapper進行分頁以及模糊查詢對比傳統XML方式進行分頁

傳統的XML分頁以及模糊查詢操作 傳統的XML方式只能使用limit以及offset進行分頁&#xff0c;通過判斷name和bindState是否為空&#xff0c;不為空則拼接條件。 List<SanitationCompanyStaff> getSanitationStaffInfo(Param("name") String name,Param("bi…

前端---需要了解瀏覽器相關知識--瀏覽器請求服務器資源---緩存

知識點1: 掘金1&#xff1a;瀏覽器緩存 掘金2 :瀏覽器緩存 一、瀏覽器緩存 請求&#xff08;靜態資源 &#xff5c; 動態資源&#xff09; 一、緩存是什么&#xff1f; 如果沒有緩存的機制 每次都要重新請求靜態資源 1.從網絡上的下載時間&#xff0c;肯定大于從硬盤里讀的…

【S32K 進階之旅】S32K 芯片的解鎖

在使用 S32K1xx MCU 的過程中&#xff0c;因為某些不當操作導致芯片被鎖、加密的情況偶有發生&#xff0c;在此總結一篇如何解鎖芯片的文檔&#xff0c;希望能夠幫到有需要的人。 1. S32K 芯片被鎖的現象及原因分析1&#xff09;在S32K 系列 MCU 開發和生產過程中&#xff…

mac os M1 安裝并啟動 postgreSQL 的問題

Homebrew 安裝 postgreSQL brew install postgresql啟動 brew services start postgresql但報錯&#xff1a; uninitialized constant Homebrew::Service::System解決方案 brew doctor按照 brew doctor 中的建議進行操作&#xff0c;如果不行&#xff0c;如下&#xff1a; h…

常用系統命令

重定向 cat aa.txt > bbb.txt 將輸出定向到bbb.txt cat aaa.txt >> bbb.txt 輸出并追加查看進程 ps ps -ef 顯示所有進程 例?&#xff1a;ps -ef | grep mysql |&#xff1a;管道符 kill pid 結束進程&#xff0c; 如 kill 3732&#xff1b;根據進程名結束進程可以先…

Qt使用qml(QtLocation)顯示地圖

一、qt版本和QtLocation模塊版本確認 如果qt版本過低的話是沒有QtLocation模塊的&#xff0c;我的版本如下 構建工具版本如下 二、qml代碼編寫 1、工程中添加模塊 首先在工程中添加模塊quickwidgets positioning location 2、添加資源文件 3、在資源文件中添加qml文件 …

Jenkins改造—nginx配置鑒權

先kill掉8082的端口進程 netstat -natp | grep 8082 kill 10256 1、下載nginx nginx安裝 EPEL 倉庫中有 Nginx 的安裝包。如果你還沒有安裝過 EPEL&#xff0c;可以通過運行下面的命令來完成安裝 sudo yum install epel-release 輸入以下命令來安裝 Nginx sudo yum inst…

windows上的docker自動化部署到服務器腳本

1、mvn install后&#xff0c;雙擊這個bat&#xff0c;實現docker build后上傳到124服務器&#xff0c;并且重啟124服務器 **echo offsetlocal:: 定義鏡像名稱和版本變量 set IMAGE_NAMEweb set IMAGE_VERSION1.3.1:: 清理本地文件 echo Cleaning up... del service-%IMAGE_N…

中大許少輝博士中國建筑出版傳媒八一新書《鄉村振興戰略下傳統村落文化旅游設計》百度百科新聞

中大許少輝博士中國建筑出版傳媒八一新書《鄉村振興戰略下傳統村落文化旅游設計》百度百科新聞&#xff1a; 鄉村振興戰略下傳統村落文化旅游設計 - 百度百科 https://baike.baidu.com/item/鄉村振興戰略下傳統村落文化旅游設計/62588677 概覽 《鄉村振興戰略下傳統村落文化旅游…

c++——靜態成員變量、靜態成員函數和const修飾的靜態成員變量

一、c靜態成員變量 在C中&#xff0c;靜態成員變量&#xff08;Static Member Variable&#xff09;是類的一種特殊類型的成員變量&#xff0c;它被類的所有實例共享&#xff0c;而不是每個實例都有自己的副本。靜態成員變量在類的所有對象之間保持唯一的狀態&#xff0c;具有…

MySQL 中文全文檢索

創建索引&#xff08;MySQL 5.7.6后全文件索引可用WITH PARSER ngram&#xff0c;針對中文&#xff0c;日文&#xff0c;韓文&#xff09; ALTER TABLE 表 ADD FULLTEXT 索引名 (字段) WITH PARSER ngram;或者CREATE FULLTEXT INDEX 索引名 ON 表 (字段) WITH PARSER ngram; …

Redisson實現分布式鎖示例

一、引入依賴 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.0</version></dependency>二、配置類 import org.redisson.Redisson; import org.redisson.api.RedissonClient;…

使用opencv進行文本增強

文本增強: import cv2 import numpy as np# 讀取圖像 image = cv2.imread(E:/image.jpg, cv2.IMREAD_GRAYSCALE)# 二值化圖像 _, binary_image = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)# 膨脹操作 kernel = np.ones((3, 3), np.uint8) dilated_…

Python Flask藍圖使用

使用示例&#xff1a; user.py from utils.sql import supabase from flask import Blueprint,request, session from utils.entity import r user_bp Blueprint(user, __name__, url_prefix/user)"""----------------------------------------API: 用戶登錄…