1. 程式人生 > >JAVA HASHMAP併發訪問出現的問題

JAVA HASHMAP併發訪問出現的問題

本文轉載自https://coolshell.cn/articles/9606.html,感謝原作者

put可能導致元素丟失

主要問題出在addEntry方法的new Entry (hash, key, value, e),如果兩個執行緒都同時取得了e,則他們下一個元素都是e,然後賦值給table元素的時候有一個成功有一個丟失。

put非null元素後get出來的卻是null

在transfer方法中程式碼如下:

void transfer(Entry[] newTable) {
    Entry[] src = table;
    int newCapacity = newTable.length
; for (int j = 0; j < src.length; j++) { Entry e = src[j]; if (e != null) { src[j] = null; do { Entry next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next
; } while (e != null); } } }

在這個方法裡,將舊陣列賦值給src,遍歷src,當src的元素非null時,就將src中的該元素置null,即將舊陣列中的元素置null了,也就是這一句:

if (e != null) {
        src[j] = null;此時若有get方法訪問這個key,它取得的還是舊陣列,當然就取不到其對應的value了。

併發訪問死迴圈

問題症狀

從前我們的Java程式碼因為一些原因使用了HashMap這個東西,但是當時的程式是單執行緒的,一切都沒有問題。後來,我們的程式效能有問題,所以需要變成多執行緒的,於是,變成多執行緒後到了線上,發現程式經常佔了100%的CPU,檢視堆疊,你會發現程式都Hang在了HashMap.get()這個方法上了,重啟程式後問題消失。但是過段時間又會來。而且,這個問題在測試環境裡可能很難重現。

我們簡單的看一下我們自己的程式碼,我們就知道HashMap被多個執行緒操作。而Java的文件說HashMap是非執行緒安全的,應該用ConcurrentHashMap。

但是在這裡我們可以來研究一下原因。

Hash表資料結構

我需要簡單地說一下HashMap這個經典的資料結構。

HashMap通常會用一個指標陣列(假設為table[])來做分散所有的key,當一個key被加入時,會通過Hash演算法通過key算出這個陣列的下標i,然後就把這個<key, value>插到table[i]中,如果有兩個不同的key被算在了同一個i,那麼就叫衝突,又叫碰撞,這樣會在table[i]上形成一個連結串列。

我們知道,如果table[]的尺寸很小,比如只有2個,如果要放進10個keys的話,那麼碰撞非常頻繁,於是一個O(1)的查詢演算法,就變成了連結串列遍歷,效能變成了O(n),這是Hash表的缺陷(可參看《Hash Collision DoS 問題》)。

所以,Hash表的尺寸和容量非常的重要。一般來說,Hash表這個容器當有資料要插入時,都會檢查容量有沒有超過設定的thredhold,如果超過,需要增大Hash表的尺寸,但是這樣一來,整個Hash表裡的無素都需要被重算一遍。這叫rehash,這個成本相當的大。

相信大家對這個基礎知識已經很熟悉了。

HashMap的rehash原始碼

下面,我們來看一下Java的HashMap的原始碼。

Put一個Key,Value對到Hash表中:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 publicV put(K key, V value) { ...... //算Hash值 inthash = hash(key.hashCode()); inti = indexFor(hash, table.length); //如果該key已被插入,則替換掉舊的value (連結操作) for(Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if(e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); returnoldValue; } } modCount++; //該key不存在,需要增加一個結點 addEntry(hash, key, value, i); returnnull; }

檢查容量是否超標

1 2 3 4

相關推薦

JAVA HASHMAP併發訪問出現的問題

本文轉載自https://coolshell.cn/articles/9606.html,感謝原作者 put可能導致元素丟失 主要問題出在addEntry方法的new Entry (hash, key, value, e),如果兩個執行緒都同時

Java Web併發訪問的執行緒安全問題

Java Web併發訪問的執行緒安全問題 2018年05月12日 02:02:52 菜鳥級的IT之路 閱讀數:68更多 個人分類: JAVA~JavaWeb 一、Servlet的執行緒安全問題 Java web伺服器下,每個Servlet只有一個例項(即

java---第一次只出現一次的字符--HashMap的應用

eat gin sub otto quest wrap bject scribe val 時間限制:1秒 空間限制:32768K 熱度指數:106210 題目描述 在一個字符串(1<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並

Java多執行緒程式設計核心技術(二)物件及變數的併發訪問

最近一直在忙比賽,四五個吧,時間有點緊張,部落格也沒時間更新~ 只能忙裡抽閒 本文屬於Java多執行緒程式設計系列的第二篇,旨在分享我對多執行緒程式設計技術的心得與感悟,順便做下筆記。 如果你閱讀完比較感興趣,歡迎關注我,等待更新後續篇章。 本文主要介紹Java多執行緒中的同步,也就是如何在Java語言中

Java 使用 CountDownLatch 模擬1000一個併發訪問

package com.zzf.concurrence.cdtest; import java.util.concurrent.CountDownLatch; public class CountdownLatchTest { private static

Java多執行緒程式設計核心技術】第二章 物件及變數的併發訪問

synchronized關鍵字 sychronized取得的鎖都是物件鎖,而不是把一段程式碼或方法(函式)當做鎖。 鎖重入功能:當一個執行緒得到一個物件鎖後,再次請求可以再次得到該物件的鎖 出現異常,鎖自動釋放 同步不具有繼承性 class Bas

Java叢集--大型網站是怎樣解決多使用者高併發訪問

Java叢集--大型網站是怎樣解決多使用者高併發訪問的       時間過得真快,再次登入部落格園來寫博,才發現距離上次的寫博時間已經過去了一個月了,雖然是因為自己找了實習,但這也說明自己對時間的掌控能力還是沒那麼的強,哈哈,看來還需不斷的努力啊!(這裡得特別說明一

java多執行緒之物件的併發訪問

1.synchronized同步方法 --1.1方法內的變數是執行緒安全的 解釋:由於方法內的變數是私有的,本體訪問的同時別人訪問不了,所以是執行緒安全的。 --1.2例項變數是非執行緒安全的 解釋:

Java多執行緒之物件及變數的併發訪問

Java物件及變數的併發訪問 當多個執行緒同時對同一個物件中的例項變數進行併發訪問時可能會產生執行緒安全問題。產生的後果就是”髒讀”,即收到的資料其實是被更改過的。 如果訪問的是方法中的變數,則不存在”非執行緒安全”問題 可以通過以下幾種方式來解決,在對物

Java併發程式設計: 使用Semaphore限制資源併發訪問的執行緒數

本文將介紹用來控制資源同時訪問個數的Semaphore工具類, 然後採用Semaphore給出一個泊車的例項,最後給出Semaphore和CountDownLatch的幾點比較.  1. Semaphore工具類介紹 Java程式碼  

多執行緒併發訪問可能出現的崩潰問題

出現這個崩潰的現象是, 進入一個介面時,該介面會同時發起三個非同步請求操作。每個操作在發起資料請求時,都可能會調某一方法進行存取某一屬性值。 如屬性comName; 該屬性宣告為:@Property (nonatomic, strong) NSString *comName

Java程式碼簡單模擬併發訪問

思路主要是通過模擬多個執行緒同時發起http請求。 public class TestBingfa { //傳送請求的url地址 private final String url =

Java併發併發下的ArrayList&&HashMap

     在平常的java程式碼開發過程中,我們會經常性的就會用到ArrayList和HashMap來輔助自己完成需求工作,而且配合的也是相當的默契。但是如果環境發生改變,在併發情況下,他是否還能

Java多執行緒程式設計核心技術--第2章 物件及變數的併發訪問

2.1 synchronized同步方法 方法中的變數不存在非執行緒安全問題,永遠都是執行緒安全的。這是方法內部的變數私有特性造成的。 2.1.2 例項變數非執行緒安全 使用多個執行緒併發訪問PrivateNum類中的addI方法。 publ

Java多執行緒——物件及變數的併發訪問

Java多線系列文章是Java多執行緒的詳解介紹,對多執行緒還不熟悉的同學可以先去看一下我的這篇部落格Java基礎系列3:多執行緒超詳細總結,這篇部落格從巨集觀層面介紹了多執行緒的整體概況,接下來的幾篇文章是對多執行緒的深入剖析。   本篇文章主要介紹Java多執行緒中的同步,也就是如何在Java語

Java併發程式設計(03):多執行緒併發訪問,同步控制

本文原始碼:[GitHub·點這裡](https://github.com/cicadasmile/java-base-parent) || [GitEE·點這裡](https://gitee.com/cicadasmile/java-base-parent) # 一、併發問題 多執行緒學習的時候,要面

java類的訪問權限

java 不同包 四種 模式 私有 pack 解析 tab enter 1.解析 Java有四種訪問權限, 其中三種有訪問權限修飾符,分別為private,public和protected,還有一種不帶任何修飾符。 private: Java語言中對訪問權限限制的最窄的修

Java中的訪問權限解析

logs java語言 mil style 解析 sta 重要 技術分享 [] 在Java中不同的對象和類擁有不同的訪問權限,所以在java中對不同的類和對象進行權限的設置顯得尤為重要. java中的權限主要分為四種,public,protect,private,和defa

Java HashMap的擴容

ins 變量 ext pla pop hold toc 函數 理解 最近博主參加面試,發現自己對於Java的HashMap的擴容過程理解不足,故最近在此進行總結。 首先說明博主德Java為1.8版本 HashMap中的變量 首先要了解HashMap的擴容過程,我們就

Java基礎——關於訪問權限的一道例題

stat package 內部 訪問修飾符 class clas tro end imp 一、回顧訪問修飾符 ==public:被它修飾的類,屬性,方法,不僅可以跨類訪問,而且可以跨包(package)訪問 ==private:可以修飾數據成員,構造方法,方法,不能修飾