国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

記一個(gè) ConcurrentHashMap 使用不當(dāng)導(dǎo)致的并發(fā)事故

開(kāi)發(fā)
對(duì)于這些映射容器的鍵使用不當(dāng)就可能導(dǎo)致唯一鍵值對(duì)多次插入的情況,所以本文將基于筆者前段時(shí)間遇到的經(jīng)典的例子為切入點(diǎn),深入剖析該問(wèn)題的原因和解決思路。

一、引言

我們都知道ConcurrentHashMap可以保證鍵值對(duì)并發(fā)插入安全,因?yàn)槠鋕ey值唯一性的原因,所以hutool對(duì)其進(jìn)行了進(jìn)一步的封裝實(shí)現(xiàn)了一個(gè)ConcurrentHashSet,代碼如下,即判斷put后是否返回null,若是null則說(shuō)明是第一次插入,反之就是存在重復(fù)元素,返回已存在的元素值。從而保證并發(fā)插入元素線程安全且唯一。

//hutool的ConcurrentHashSet通過(guò)判斷返回null得知之前是否插入過(guò)重復(fù)元素
@Override
 public boolean add(E e) {
  return map.put(e, PRESENT) == null;
 }

但是如果對(duì)于這些映射容器的鍵使用不當(dāng)就可能導(dǎo)致唯一鍵值對(duì)多次插入的情況,所以本文將基于筆者前段時(shí)間遇到的經(jīng)典的例子為切入點(diǎn),深入剖析該問(wèn)題的原因和解決思路。

二、詳解ConcurrentHashMap并發(fā)重復(fù)插入問(wèn)題

1. 需求說(shuō)明

我們現(xiàn)在有這樣一個(gè)需求,大體是通過(guò)數(shù)據(jù)庫(kù)獲取要處理的任務(wù)并按照如下步驟執(zhí)行:

  • 從數(shù)據(jù)庫(kù)讀取未完成(status為0)的任務(wù),將其采用并發(fā)容器(ConcurrentHashSet)存放,key為這個(gè)任務(wù)對(duì)象
  • 工作線程處理,并在內(nèi)存中將其設(shè)置為1
  • 定時(shí)任務(wù)線程從容器中讀取這些任務(wù)并移除
  • 將已完成任務(wù)狀態(tài)寫(xiě)回庫(kù)中

2. 落地代碼

對(duì)應(yīng)任務(wù)表的實(shí)體類封裝如下,我們的加載到ConcurrentHashSet會(huì)被多個(gè)線程并發(fā)的調(diào)度處理,處理過(guò)程中會(huì)并發(fā)更新?tīng)顟B(tài)。

@Data
publicclass Task {
    
    privateint id;

    /**
     * 任務(wù)名稱
     */
    private String taskName;

    /**
     * 0.未開(kāi)始
     * 1.進(jìn)行中
     * 2.已完成
     */
    privateint status;


}

對(duì)應(yīng)的實(shí)現(xiàn)代碼如下,可以看到從數(shù)據(jù)庫(kù)讀取未開(kāi)始的任務(wù),線程1將其更新為處理完成后更新為處理中,線程2處理完成后更新為已完成:

public static void main(String[] args) throws InterruptedException {
        ConcurrentHashSet<Task> set = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(2);

        //假設(shè)從數(shù)據(jù)庫(kù)讀取一個(gè)task
        Task task = new Task();
        task.setId(1);
        task.setTaskName("任務(wù)1");
        task.setStatus(0);
        set.add(task);


        //模擬多線程并發(fā)更新

        //線程1更新為處理中
        new Thread(() -> {
            log.info("線程1處理中....");
            task.setStatus(1);
            set.add(task);

            countDownLatch.countDown();
        }, "t1").start();

        //線程2更新為已完成
        new Thread(() -> {
            log.info("線程2處理中....");
            task.setStatus(2);
            set.add(task);

            countDownLatch.countDown();
        }, "t2").start();


        countDownLatch.await();

        log.info("set size:{}", set.size());
    }

輸出結(jié)果如下,可以看到明明同一個(gè)對(duì)象,結(jié)果插入了3次:

00:44:32.637 [main] INFO com.sharkChili.webTemplate.Main - set size:3

調(diào)試查看set內(nèi)部,3個(gè)元素都指向我們的唯一的任務(wù)-1。

3. 事故原因

我們都知道JDK8版本無(wú)論是HashMap還是ConcurrentHashMap底層采用數(shù)組+鏈表/紅黑樹(shù),元素進(jìn)行插入前都需要進(jìn)行hash運(yùn)算定位數(shù)組索引,然后使用equal和hashCode比較的過(guò)程元素是否存在。 很明顯,我們上文并發(fā)操作元素時(shí)修改了status字典,導(dǎo)致每次得出的hashCode結(jié)果值改變了,進(jìn)而導(dǎo)致同一個(gè)元素因?yàn)椴煌膆ashCode插入到不同的位置,出現(xiàn)去重失?。?/p>

對(duì)應(yīng)筆者也給出ConcurrentHashMap的put方法底層實(shí)現(xiàn):

final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) thrownew NullPointerException();
        //計(jì)算key的hash值,因?yàn)槲覀儎?dòng)態(tài)修改了status導(dǎo)致hash值不同
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            //因?yàn)閔ash值不同每次定位到的i位置不同,最終存到不同的位置
            elseif ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
  }
  .....
}

4. 解決方案

很明顯出現(xiàn)這個(gè)問(wèn)題的原因就是因?yàn)椴l(fā)操作修改的status影響了hashcode計(jì)算結(jié)果,進(jìn)而導(dǎo)致并發(fā)操作變得無(wú)效,因?yàn)閕d是全局唯一的,所以直接重寫(xiě)hashCode和equals方法,讓Task對(duì)象的計(jì)算和比對(duì)都通過(guò)id進(jìn)行:

@Data
publicclass Task {

//......略

   @Override
    public boolean equals(Object o) {
        if (this == o) returntrue;
        if (o == null || getClass() != o.getClass()) returnfalse;
        Task task = (Task) o;
        return id == task.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

三、小結(jié)

總的來(lái)說(shuō),對(duì)于這類涉及并發(fā)操作的重構(gòu),建議梳理清晰的數(shù)據(jù)流向并結(jié)合源碼工作流程加以推斷分析,最終明確問(wèn)題風(fēng)險(xiǎn)點(diǎn)直接進(jìn)行邏輯修復(fù)并及時(shí)提測(cè)。

責(zé)任編輯:趙寧寧 來(lái)源: 寫(xiě)代碼的SharkChili
相關(guān)推薦

2025-07-16 07:20:00

開(kāi)發(fā)代碼并發(fā)

2021-09-11 19:00:54

Intro元素MemoryCache

2024-09-05 08:07:55

2022-10-25 18:00:00

Redis事務(wù)生產(chǎn)事故

2019-10-10 15:40:17

redisbug數(shù)據(jù)庫(kù)

2024-06-28 10:01:04

2022-06-21 11:24:05

多線程運(yùn)維

2021-06-10 06:59:34

Redis應(yīng)用API

2024-02-04 08:26:38

線程池參數(shù)內(nèi)存

2020-10-22 07:09:19

TCP網(wǎng)絡(luò)協(xié)議

2021-05-20 10:02:50

系統(tǒng)Redis技巧

2009-12-17 14:53:52

VS2008程序

2021-08-26 14:26:25

Java代碼集合

2010-01-06 10:56:47

華為交換機(jī)使用

2020-11-16 12:35:25

線程池Java代碼

2021-07-11 09:34:45

ArrayListLinkedList

2011-08-18 13:49:32

筆記本技巧

2022-04-08 08:48:16

線上事故日志訂閱者

2024-11-20 18:16:39

MyBatis批量操作數(shù)據(jù)庫(kù)

2024-08-20 21:27:04

docker部署容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

中文字幕在线免费专区| 欧美日韩大尺度| 成人在线视频区| 精品国产视频在线| 日本一道高清亚洲日美韩| 欧美激情一区二区三区久久久| 精品一区二区三区中文字幕在线 | 日韩精品免费在线观看| 中文在线字幕免费观看| 日韩成人激情视频| 日韩专区视频| 欧美精品久久久久久久| 91精品国产91久久久久久黑人| 国产精品久久一区| 韩国一区二区三区在线观看| 亚洲日本精品一区| 中文一区二区完整视频在线观看| 最新国产在线视频| 欧美性色欧美a在线播放| www.欧美视频| 97超碰蝌蚪网人人做人人爽| 女人天堂亚洲aⅴ在线观看| 亚洲自拍偷拍二区| 1000精品久久久久久久久| 中文字幕av在线播放| 欧美成人第一页| 在线欧美日韩| 羞羞的视频网站| 欧美午夜电影网| 精品国产乱码久久久久久樱花| 国产成人综合精品| 国产亚洲精品精华液| 欧美顶级大胆免费视频| 国产成人精品一区二区三区福利| 91在线免费视频观看| 国内精品久久久久久野外| 久久精品99久久久香蕉| 日韩精品一区第一页| 五月亚洲综合| 一区二区三区国产在线观看| 欧美日本国产| 两个人hd高清在线观看| 精品中文字幕乱| 麻豆精品蜜桃视频网站| 一区二区三区视频在线观看视频| 韩日精品中文字幕| 欧美国产精品一区| 日韩中文影院| 四虎永久国产精品| 欧美午夜精品久久久久久孕妇| 成人在线国产| 国产香蕉尹人视频在线| 欧美一级免费视频| 一二三四社区欧美黄| 亚洲一区二区三区中文字幕在线观看 | 亚洲视频第一页| 偷拍欧美精品| 少妇性bbb搡bbb爽爽爽欧美| 欧美最猛性xxxxx(亚洲精品)| 国产精品视频一区二区三区不卡| 成人午夜sm精品久久久久久久| www国产免费| 欧美肥婆姓交大片| 久久久精品黄色| 在线精品小视频| www.成人.com| 欧美精品国产精品久久久 | 国产精品香蕉在线观看| 污片在线观看一区二区| 欧美freesextv| 国产在线播放av| 亚洲成人a**址| 一区二区三区视频免费在线观看| 久久久蜜桃精品| 国产日产精品_国产精品毛片| 加勒比一区二区三区在线| 欧美精品一区三区在线观看| 国产成人97精品免费看片| 欧美中文字幕不卡| 黑人精品欧美一区二区蜜桃| 欧美调教在线| 成人在线黄色电影| 麻豆免费网站| 裸体大乳女做爰69| 国产97色在线| 亚洲美女av网站| 亚洲午夜免费电影| 国产酒店精品激情| 蜜桃一区二区三区| 成人国产一区二区三区精品麻豆| 国内外成人免费在线视频| 成人在线看片| 热99在线视频| 中文字幕欧美精品在线| 欧美亚洲一区二区在线| 成a人片国产精品| 久久九九99| 激情久久久久久久| 日韩av网站在线免费观看| 国产黄色小视频在线| 麻豆电影在线| 免费大片在线观看| 狠狠干视频网站| 精品综合久久久| 国产精品久久久久久久久久免费| 日韩在线资源网| 亚洲精品一线二线三线| 欧美日韩免费区域视频在线观看| 成人小视频免费观看| 蜜桃一区二区三区在线观看| 婷婷综合五月| 欧美黑白配在线| 亚洲欧美小说色综合小说一区| 国产色a在线| 国产专区在线播放| 在线视频网站| 人成在线免费视频| 国产区视频在线播放| 黄网免费视频| 成人av视屏| 922tv免费观看在线| 91传媒在线观看| 粗大的内捧猛烈进出在线视频| 91嫩草在线播放| 在线影院福利| 飘雪影院手机免费高清版在线观看 | www.男人的天堂.com| 国产原创popny丨九色| av网站在线不卡| 黄色激情网站| 青柠在线影院观看日本| 激情视频网址| 精品美女视频在线观看免费软件| 亚洲尤物在线视频| aa在线视频| 精品中文在线| 精品一区电影| 欧美女人交a| 国产欧美日韩综合| 欧美日韩国产高清一区二区三区 | 看av免费毛片手机播放| www.中文字幕在线| 色综合成人av| 最新中文字幕在线播放| 日韩一区二区三免费高清在线观看| 国产精品宾馆| 欧美三级特黄| 国产盗摄精品一区二区三区在线| 久久精品人人做人人综合| 日韩欧美国产骚| 日韩中文字幕视频| 91青青草免费在线看| 天堂精品一区二区三区| 特黄特黄的视频| 在线观看老湿视频福利| 17videosex性欧美| 久久免费大视频| 国产亚洲欧美日韩俺去了| 在线欧美一区二区| 美女黄色丝袜一区| 欧洲一区二区日韩在线视频观看免费 | 国产成人精品123区免费视频| 久久精品九色| 三级欧美在线一区| 亚洲精品欧美在线| 亚洲国产精品电影在线观看| 国产精品成人播放| 91视频最新入口| 在线免费观看a视频| 经典一区二区| 国产精品电影一区二区| 亚洲欧美激情视频| 91在线看www| 污网站在线看| 欧美日韩卡一| 精品一区二区三区在线观看| 一本在线高清不卡dvd| 国产精品黄视频| 日本在线免费观看视频| 久久久久亚洲精品中文字幕| 激情六月婷婷久久| 亚洲欧美一区二区三区四区| 伊人av成人| 国产一区二区三区朝在线观看| 肉色丝袜一区二区| 91国偷自产一区二区开放时间| 成人精品在线视频| 自拍偷拍21p| 国产精品zjzjzj在线观看| 91美女片黄在线观看| 久久久精品电影| 国产成人精品无码播放| 色狼人综合干| 精品国产户外野外| 91社区国产高清| 韩国免费在线视频| 国产在线不卡视频| 久久99精品久久久久久青青91| 日日摸日日碰夜夜爽无码| 亚洲va欧美va人人爽成人影院|