最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JDK1.7的ConcurrentHashMap源碼解析

 更新時間:2023年12月08日 10:25:32   作者:努力的小強  
這篇文章主要介紹了JDK1.7的ConcurrentHashMap源碼解析,HashMap是非線程安全的,而HashTable是線程安全的,但是HashTable實現(xiàn)同步的方法比較暴力,即在所有的方法體上添加synchronized關鍵字,需要的朋友可以參考下

概述

HashMap是非線程安全的,而HashTable是線程安全的,但是HashTable實現(xiàn)同步的方法比較暴力,即在所有的方法體上添加synchronized關鍵字

相當于所有讀寫線程均去讀取一把鎖,從并發(fā)角度,HashTable其實無法滿足較高的并發(fā)度。

另一種同步Map的方法是使用Collections工具類。

    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }
    /**
     * @serial include
     */
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;
        private final Map<K,V> m;     // Backing Map
        //互斥鎖
        final Object      mutex;        // Object on which to synchronize
        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }
        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }
        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }
        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        public void putAll(Map<? extends K, ? extends V> map) {
            synchronized (mutex) {m.putAll(map);}
        }
        public void clear() {
            synchronized (mutex) {m.clear();}
        }
        private transient Set<K> keySet;
        private transient Set<Map.Entry<K,V>> entrySet;
        private transient Collection<V> values;
        public Set<K> keySet() {
            synchronized (mutex) {
                if (keySet==null)
                    keySet = new SynchronizedSet<>(m.keySet(), mutex);
                return keySet;
            }
        }
        public Set<Map.Entry<K,V>> entrySet() {
            synchronized (mutex) {
                if (entrySet==null)
                    entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
                return entrySet;
            }
        }
        public Collection<V> values() {
            synchronized (mutex) {
                if (values==null)
                    values = new SynchronizedCollection<>(m.values(), mutex);
                return values;
            }
        }
        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return m.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return m.hashCode();}
        }
        public String toString() {
            synchronized (mutex) {return m.toString();}
        }
        // Override default methods in Map
        @Override
        public V getOrDefault(Object k, V defaultValue) {
            synchronized (mutex) {return m.getOrDefault(k, defaultValue);}
        }
        @Override
        public void forEach(BiConsumer<? super K, ? super V> action) {
            synchronized (mutex) {m.forEach(action);}
        }
        @Override
        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            synchronized (mutex) {m.replaceAll(function);}
        }
        @Override
        public V putIfAbsent(K key, V value) {
            synchronized (mutex) {return m.putIfAbsent(key, value);}
        }
        @Override
        public boolean remove(Object key, Object value) {
            synchronized (mutex) {return m.remove(key, value);}
        }
        @Override
        public boolean replace(K key, V oldValue, V newValue) {
            synchronized (mutex) {return m.replace(key, oldValue, newValue);}
        }
        @Override
        public V replace(K key, V value) {
            synchronized (mutex) {return m.replace(key, value);}
        }
        @Override
        public V computeIfAbsent(K key,
                Function<? super K, ? extends V> mappingFunction) {
            synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);}
        }
        @Override
        public V computeIfPresent(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);}
        }
        @Override
        public V compute(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.compute(key, remappingFunction);}
        }
        @Override
        public V merge(K key, V value,
                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.merge(key, value, remappingFunction);}
        }
        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

這種方法與HashTable實現(xiàn)方式類似,也是鎖住整表來實現(xiàn)同步的。而ConcurrentHashMap則避免了上述兩種Map同步方式鎖住全表的問題。

ConcurrentHashMap可以做到讀取數據不加鎖,并且其內部的結構可以讓其在進行寫操作的時候能夠將鎖的粒度保持盡量的小,不用對整個ConcurrentHashMap加鎖。

ConcurrentHashMap內部結構

ConcurrentHashMap內部采用了一種叫segment的數據結構,很明顯它就是一個哈希桶數組,數組的元素就是HashEntry。

在這里插入圖片描述

ConcurrentHashMap比HashMap多了一次hash過程,第一次hash定位到Segment,第二次hash定位到HashEntry,然后鏈表搜索找到指定節(jié)點。

該實現(xiàn)方法的缺點是hash過程比普通的HashMap要長,但是優(yōu)點也很明顯,在進行寫操作時,只需鎖住寫元素所在的Segment即可,其他Segment無需加鎖,提高了并發(fā)讀寫的效率。

Segment

Segment繼承了ReentrantLock并實現(xiàn)了序列化接口,說明Segment的鎖是可以重入的。

在這里插入圖片描述

    static final class Segment<K,V> extends ReentrantLock implements Serializable {
        transient volatile HashEntry<K,V>[] table;
        transient int count;
        transient int modCount;
        transient int threshold;
        final float loadFactor;
  • count:Segment中元素的數量
  • modCount:對table的大小造成影響的操作的數量(比如put或者remove操作)
  • threshold:擴容閾值
  • table:鏈表數組,數組中的每一個元素代表了一個鏈表的頭部
  • loadFactor:負載因子

Segment的數據結構與普通的HashMap基本類似,只是通過繼承ReentrantLock可實現(xiàn)線程安全的操作。

HashEntry

Segment中的元素是以HashEntry的形式存放在鏈表數組中的,其結構與普通HashMap的HashEntry基本一致,不同的是Segment的HashEntry的value由volatile修飾,以支持內存可見性,即寫操作對其他讀線程即時可見。

    static final class HashEntry<K,V> {
        final int hash;
        final K key;
        volatile V value;
        volatile HashEntry<K,V> next;
    }

ConcurrentHashMap構造器

//initialCapacity:初始容量
	//loadFactor:負載因子
	//concurrencyLevel:ConcurrentHashMap內部的Segment的數量
    public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        //若concurrencyLevel大于MAX_SEGMENTS,則concurrencyLevel=MAX_SEGMENTS
        //保證最大并發(fā)不超過MAX_SEGMENTS(1<<16)
        if (concurrencyLevel > MAX_SEGMENTS)
            concurrencyLevel = MAX_SEGMENTS;
        //求解concurrencyLevel與2的幾次方最近
        //如concurrencyLevel=5 則5與2^3=8最近 則sshift=8 ssize=3
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;
        }
        //segmentShift和segmentMask主要用于元素的hash
        this.segmentShift = 32 - sshift;
        this.segmentMask = ssize - 1;
        //ConcurrentHashMap初始容量不超過MAXIMUM_CAPACITY(1<<30)
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //根據ConcurrentHashMap總容量initialCapacity除以
        //Segment[]數組的長度得到單個分段鎖segment中HashEntry[]的大小
        int c = initialCapacity / ssize;
        //保證分段鎖segment的總容量c不小于初始的容量
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        //cap為每個segment的初始容量,其值為離c天花板方向最近的2^n
        //例:c為5 cap為8 c為12 cap為16
        while (cap < c)
            cap <<= 1;
        // 創(chuàng)建Segment
        Segment<K,V> s0 =
            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                             (HashEntry<K,V>[])new HashEntry[cap]);
        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;
    }

ConcurrentHashMap put()源碼分析

put()方法向ConcurrentHashMap中添加元素

    public V put(K key, V value) {
        Segment<K,V> s;
        //value不能為空
        if (value == null)
            throw new NullPointerException();
        //計算key的hash值
        int hash = hash(key);
        //無符號右移segmentShift(默認16)位
        //然后& segmentMask(默認15)得到segment在內存中的位置
        int j = (hash >>> segmentShift) & segmentMask;
         如果Segment不存在,則調用ensureSegment方法
        if ((s = (Segment<K,V>)UNSAFE.getObject
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            //初始化segment
            s = ensureSegment(j);
        //放值
        return s.put(key, hash, value, false);
    }

Segment put()方法源碼解析

        final V put(K key, int hash, V value, boolean onlyIfAbsent) {
        	// 嘗試直接獲取鎖,獲取到鎖node為null,
        	//否則調用scanAndLockForPut方法
            HashEntry<K,V> node = tryLock() ? null :
                scanAndLockForPut(key, hash, value);
            V oldValue;
            try {
                HashEntry<K,V>[] tab = table;
                // 獲取在tab數組中的位置
                int index = (tab.length - 1) & hash;
                // 得到鏈表的頭節(jié)點
                HashEntry<K,V> first = entryAt(tab, index);
                // 遍歷鏈表
                for (HashEntry<K,V> e = first;;) {
                    if (e != null) {
                        K k;
                        if ((k = e.key) == key ||
                            (e.hash == hash && key.equals(k))) {
                            oldValue = e.value;
                            if (!onlyIfAbsent) {
                                e.value = value;
                                ++modCount;
                            }
                            break;
                        }
                        e = e.next;
                    }
                    // 遍歷到鏈表尾部,沒有重復的key,則新插入
                    else {
                        if (node != null)
                        	// 頭插法,將node節(jié)點設為鏈表頭節(jié)點
                            node.setNext(first);
                        else
                        	// 為null,則新建一個節(jié)點
                            node = new HashEntry<K,V>(hash, key, value, first);
                        int c = count + 1;
                        // 若c超過閾值則擴容,并且數組長度小于MAXIMUM_CAPACITY = 1 << 30
                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                        	// 擴容并進行重新hash
                            rehash(node);
                        else
                            setEntryAt(tab, index, node);
                        ++modCount;
                        count = c;
                        oldValue = null;
                        break;
                    }
                }
            } finally {
                unlock();
            }
            return oldValue;
        }

scanAndLockForPut

private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
	// 獲取鏈表頭結點
	HashEntry<K,V> first = entryForHash(this, hash);
	HashEntry<K,V> e = first;
	HashEntry<K,V> node = null;
	int retries = -1; // negative while locating node
	// 不斷嘗試獲取鎖
	while (!tryLock()) {
		HashEntry<K,V> f; // to recheck first below
		if (retries < 0) {
			// 鏈表的頭結點為null,或者遍歷到鏈表的尾部
			if (e == null) {
				// 這里加條件是因為,有可能已經初始化node節(jié)點了
				// 結果由于頭結點改變重新遍歷鏈表
				if (node == null) // speculatively create node
					node = new HashEntry<K,V>(hash, key, value, null);
				retries = 0;
			}
			// 找到相同key的節(jié)點
			else if (key.equals(e.key))
				retries = 0;
			// 沒有找到key對應的節(jié)點,指向下一個節(jié)點
			else
				e = e.next;
		}
		// 可用處理器數量大于1,MAX_SCAN_RETRIES=64,否則為1
		else if (++retries > MAX_SCAN_RETRIES) {
			// 調用ReentrantLock中NonfairSync的lock()方法
			// 執(zhí)行過程中有可能不阻塞獲取到鎖,也有可能被阻塞
			// 而不是之前的一直嘗試直接獲取鎖
			lock();
			break;
		}
		// 鏈表的頭結點發(fā)生變化,更新頭結點,并重置retries值為-1
		else if ((retries & 1) == 0 &&
				 (f = entryForHash(this, hash)) != first) {
			e = first = f; // re-traverse if entry changed
			retries = -1;
		}
	}
	return node;
}

到此這篇關于JDK1.7的ConcurrentHashMap源碼解析的文章就介紹到這了,更多相關ConcurrentHashMap源碼內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java保留兩位小數的實現(xiàn)方法

    Java保留兩位小數的實現(xiàn)方法

    這篇文章主要介紹了 Java保留兩位小數的實現(xiàn)方法的相關資料,需要的朋友可以參考下
    2017-06-06
  • Spring Cloud Alibaba Nacos Config進階使用

    Spring Cloud Alibaba Nacos Config進階使用

    這篇文章主要介紹了Spring Cloud Alibaba Nacos Config進階使用,文中使用企業(yè)案例,圖文并茂的展示了Nacos Config的使用,感興趣的小伙伴可以看一看
    2021-08-08
  • Java里得到00:00:00格式的時分秒的Timestamp

    Java里得到00:00:00格式的時分秒的Timestamp

    Java里如何得到00:00:00格式的時分秒的Timestamp ,下面是具體的實現(xiàn)代碼,需要的朋友可以參考下。
    2009-09-09
  • java數組基礎詳解

    java數組基礎詳解

    這篇文章主要介紹了Java數組基礎詳解,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • Java中的Map集合簡單匯總解析

    Java中的Map集合簡單匯總解析

    這篇文章主要介紹了Java中的Map集合簡單匯總解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 詳解Java中方法next()和nextLine()的區(qū)別與易錯點

    詳解Java中方法next()和nextLine()的區(qū)別與易錯點

    這篇文章主要介紹了詳解Java中方法next()和nextLine()的區(qū)別與易錯點,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • SpringBoot整合Thymeleaf的方法

    SpringBoot整合Thymeleaf的方法

    這篇文章主要介紹了SpringBoot整合Thymeleaf的方法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下,希望能夠幫助到你
    2021-07-07
  • JAVA調用SAP WEBSERVICE服務實現(xiàn)流程圖解

    JAVA調用SAP WEBSERVICE服務實現(xiàn)流程圖解

    這篇文章主要介紹了JAVA調用SAP WEBSERVICE服務實現(xiàn)流程圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-10-10
  • 獲取Java線程轉儲的常用方法(推薦)

    獲取Java線程轉儲的常用方法(推薦)

    這篇文章主要介紹了獲取Java線程轉儲的常用方法,本文給大家介紹的非常想詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • 使用Java實現(xiàn)驗證碼程序

    使用Java實現(xiàn)驗證碼程序

    這篇文章主要為大家詳細介紹了使用Java實現(xiàn)驗證碼程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04

最新評論

清新县| 山东省| 白水县| 香河县| 上饶县| 塔河县| 旅游| 甘德县| 大邑县| 文登市| 青河县| 武陟县| 左贡县| 逊克县| 肃北| 舞钢市| 桓仁| 新建县| 云南省| 社会| 乐昌市| 乌恰县| 台北县| 富川| 景洪市| 永顺县| 广州市| 安陆市| 土默特左旗| 漳浦县| 周宁县| 南靖县| 原平市| 合江县| 余姚市| 来凤县| 城固县| 新民市| 莱阳市| 买车| 屯留县|