Java并發編程之并發容器ConcurrentHashMap(JDK1.8)解析

這個版本ConcurrentHashMap難度提升了很多,就簡單的談一下常用的方法就好了,可能有些講的不太清楚,麻煩發現的大佬指正一下

主要數據結構

1.8將Segment取消了,保留了table數組的形式,但是不在以HashEntry純鏈表的形式儲存數據了,采用了鏈表+紅黑樹的形式儲存數據;在使用get()方法時,使用純鏈表的時間復雜度時O(n),而在使用紅黑樹的數據結構時,時間復雜度為O(logn),在查詢的速度上有很大的提升;但是在創建的時候并非直接使用紅黑樹儲存數據,而是依舊采用鏈表存儲,但是但鏈表的長度超過8的時候就會轉換成紅黑樹數據結構。

Node

依舊還是跟HashEntry的數據結構一致,采用鏈表的數據結構存儲

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;volatile V val;volatile Node<K,V> next;Node(int hash, K key, V val, Node<K,V> next) {this.hash = hash;this.key = key;this.val = val;this.next = next;}//.....}

TreeNode

紅黑樹的數據結構原型,繼承了Node

     /*** Nodes for use in TreeBins*/static final class TreeNode<K,V> extends Node<K,V> {TreeNode<K,V> parent;  // red-black tree linksTreeNode<K,V> left;TreeNode<K,V> right;TreeNode<K,V> prev;    // needed to unlink next upon deletionboolean red;TreeNode(int hash, K key, V val, Node<K,V> next,TreeNode<K,V> parent) {super(hash, key, val, next);this.parent = parent;}Node<K,V> find(int h, Object k) {return findTreeNode(h, k, null);}//......}

TreeBin

table數組中儲存的就是TreeBin對象,存儲了TreeNode<K,V>的根節點

     static final class TreeBin<K,V> extends Node<K,V> {TreeNode<K,V> root;volatile TreeNode<K,V> first;volatile Thread waiter;volatile int lockState;// values for lockStatestatic final int WRITER = 1; // set while holding write lockstatic final int WAITER = 2; // set when waiting for write lockstatic final int READER = 4; // increment value for setting read lock//...}

構造方法

在構造方法中,并沒有做什么操作,僅僅是設置了一個屬性值sizeCtl(也是容器的控制器)

sizeCtl:

負數:表示進行初始化或者擴容,-1表示正在初始化,-N,表示有N-1個線程正在進行擴容。

正數:0 表示還沒有被初始化,>0的數,初始化或者是下一次進行擴容的閾值。

而實際的初始化是在put()方法中加載table數組

       /*** The array of bins. Lazily initialized upon first insertion.* Size is always a power of two. Accessed directly by iterators.*/transient volatile Node<K,V>[] table;/*** Table initialization and resizing control.  When negative, the* table is being initialized or resized: -1 for initialization,* else -(1 + the number of active resizing threads).  Otherwise,* when table is null, holds the initial table size to use upon* creation, or 0 for default. After initialization, holds the* next element count value upon which to resize the table.*/private transient volatile int sizeCtl;  /*** Creates a new, empty map with the default initial table size (16).*/public ConcurrentHashMap() {}/*** Creates a new, empty map with an initial table size* accommodating the specified number of elements without the need* to dynamically resize.** @param initialCapacity The implementation performs internal* sizing to accommodate this many elements.* @throws IllegalArgumentException if the initial capacity of* elements is negative*/public ConcurrentHashMap(int initialCapacity) {if (initialCapacity < 0)throw new IllegalArgumentException();int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?MAXIMUM_CAPACITY :tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));this.sizeCtl = cap;}

put()方法

具體調用了putVal(),依舊還是和putIfAbsent()調用的是同一個方法,ConcurrentHashMap容器初始化實在put()方法中加載的即initTable();方法;

這個版本計算hash值的方法為spread(object.hashCode()),在創建完table數組之后,接下來就是創建數組中的Node節點了,會判斷當前是鏈表還是紅黑樹,然后將數據插入到對應的鏈表或樹中,鏈表插入一個數據binCount就會自增,然后當這個值大于一個閾值時就會進入到鏈表轉紅黑樹方法treeifyBin

    /*** Initializes table, using the size recorded in sizeCtl.*/
//采用了CAS設置了sizeCtl的值private final Node<K,V>[] initTable() {Node<K,V>[] tab; int sc;while ((tab = table) == null || tab.length == 0) {if ((sc = sizeCtl) < 0)Thread.yield(); // lost initialization race; just spinelse if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {try {if ((tab = table) == null || tab.length == 0) {int n = (sc > 0) ? sc : DEFAULT_CAPACITY;@SuppressWarnings("unchecked")//創建table數組Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];table = tab = nt;//sc = 0.75n,即擴容因子還是0.75sc = n - (n >>> 2);}} finally {sizeCtl = sc;}break;}}return tab;}//計算key的hash值,與1.7相比,更加均勻static final int spread(int h) {return (h ^ (h >>> 16)) & HASH_BITS;}
/** Implementation for put and putIfAbsent */final V putVal(K key, V value, boolean onlyIfAbsent) {if (key == null || value == null) throw new NullPointerException();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();else if ((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}else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {V oldVal = null;synchronized (f) {if (tabAt(tab, i) == f) {if (fh >= 0) {//進入到鏈表binCount = 1;for (Node<K,V> e = f;; ++binCount) {K ek;if (e.hash == hash &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}Node<K,V> pred = e;if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key,value, null);break;}}}else if (f instanceof TreeBin) {//進入到紅黑樹Node<K,V> p;binCount = 2;if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);return null;}/*** Replaces all linked nodes in bin at given index unless table is* too small, in which case resizes instead.*///將Node<>[]中的鏈表換成紅黑樹的TreeBinprivate final void treeifyBin(Node<K,V>[] tab, int index) {Node<K,V> b; int n, sc;if (tab != null) {if ((n = tab.length) < MIN_TREEIFY_CAPACITY)tryPresize(n << 1);else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {synchronized (b) {if (tabAt(tab, index) == b) {TreeNode<K,V> hd = null, tl = null;for (Node<K,V> e = b; e != null; e = e.next) {TreeNode<K,V> p =new TreeNode<K,V>(e.hash, e.key, e.val,null, null);if ((p.prev = tl) == null)hd = p;elsetl.next = p;tl = p;}setTabAt(tab, index, new TreeBin<K,V>(hd));}}}}}

在put()的時候有個擴容的方法helpTransfer(tab, f);?

    /*** Helps transfer if a resize is in progress.*/final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {Node<K,V>[] nextTab; int sc;if (tab != null && (f instanceof ForwardingNode) &&(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {int rs = resizeStamp(tab.length);while (nextTab == nextTable && table == tab &&(sc = sizeCtl) < 0) {if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||sc == rs + MAX_RESIZERS || transferIndex <= 0)break;if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {//擴容table數組transfer(tab, nextTab);break;}}return nextTab;}return table;}

get()

首先獲取到key值的hash值,然后去定位是數組中的哪個Node節點,然后去遍歷鏈表或者紅黑樹查找;

    public V get(Object key) {Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;int h = spread(key.hashCode());//獲取key的對應的hash值if ((tab = table) != null && (n = tab.length) > 0 &&(e = tabAt(tab, (n - 1) & h)) != null) {if ((eh = e.hash) == h) {//判斷是否為當前數組元素if ((ek = e.key) == key || (ek != null && key.equals(ek)))return e.val;}//從鏈表中獲取數據else if (eh < 0)return (p = e.find(h, key)) != null ? p.val : null;//從紅黑樹中獲取while ((e = e.next) != null) {if (e.hash == h &&((ek = e.key) == key || (ek != null && key.equals(ek))))return e.val;}}return null;}

結語:這玩意難度有點高啊,想要真正的看懂還需努力!

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

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

相關文章

simulink顯示多個數據_如何在 Simulink 中使用 PID Tuner 進行 PID 調參?

作者 | 安布奇責編 | 胡雪蕊出品 | CSDN(ID: CSDNnews)本文為一篇技術干貨&#xff0c;主要講述在Simulink如何使用PID Tuner進行PID調參。PID調參器( PIDTuner)概述1.1 簡介使用PID Tuner可以對Simulink模型中的PID控制器&#xff0c;離散PID控制器&#xff0c;兩自由度PID控制…

Java并發編程之堵塞隊列介紹以及SkipList(跳表)

堵塞隊列 先了解一下生產者消費者模式&#xff1a; 生產者就是生產數據的一方&#xff0c;消費者就是消費數據的另一方。在多線程開發中&#xff0c;如果生產者處理速度很快&#xff0c;而消費者處理速度很慢&#xff0c;那么生產者就必須等待消費者處理完&#xff0c;才能繼…

python生成list的時候 可以用lamda也可以不用_python 可迭代對象,迭代器和生成器,lambda表達式...

分頁查找#5.隨意寫一個20行以上的文件(divmod)# 運行程序&#xff0c;先將內容讀到內存中&#xff0c;用列表存儲。# l []# 提示&#xff1a;一共有多少頁# 接收用戶輸入頁碼&#xff0c;每頁5條&#xff0c;僅輸出當頁的內容def read_page(bk_list,n,endlineNone):startline …

數據挖掘技術簡介[轉]

關鍵詞&#xff1a; 關鍵詞&#xff1a;數據挖掘 數據集合 1. 引言  數據挖掘(Data Mining)是從大量的、不完全的、有噪聲的、模糊的、隨機的數據中提取隱含在其中的、人們事先不知道的、但又是潛在有用的信息和知識的過程。隨…

樹莓派安裝smbus_樹莓派使用smbus不兼容問題(no module named 'smbus')

樹莓派使用smbus不兼容問題(no module named ‘smbus’)python3.5–3.6可以使用smbus2代替smbus1. 先參考以下方法&#xff1a;github討論樹莓派社區2.Pypi上可以下載smbus2smbus2PyPi介紹&#xff1a;當前支持的功能有&#xff1a;獲取i2c功能(I2C_FUNCS)read_bytewrite_byter…

Java并發編程之線程池ThreadPoolExecutor解析

線程池存在的意義 平常使用線程即new Thread()然后調用start()方法去啟動這個線程&#xff0c;但是在頻繁的業務情況下如果在生產環境大量的創建Thread對象是則會浪費資源&#xff0c;不僅增加GC回收壓力&#xff0c;并且還浪費了時間&#xff0c;創建線程是需要花時間的&…

面向過程的門面模式

{*******************************************************}{ }{ 業務邏輯一 }{ }{ 版權所有 (C) 2008 陳…

Java并發編程之線程定時器ScheduledThreadPoolExecutor解析

定時器 就是需要周期性的執行任務&#xff0c;也叫調度任務&#xff0c;在JDK中有個類Timer是支持周期性執行&#xff0c;但是這個類不建議使用了。 ScheduledThreadPoolExecutor 繼承自ThreadPoolExecutor線程池&#xff0c;在Executors默認創建了兩種&#xff1a; newSin…

python xml轉換鍵值對_Python 提取dict轉換為xml/json/table并輸出

#!/usr/bin/python#-*- coding:gbk -*-#設置源文件輸出格式import sysimport getoptimport jsonimport createDictimport myConToXMLimport myConToTabledef getRsDataToDict():#獲取控制臺中輸入的參數&#xff0c;并根據參數找到源文件獲取源數據csDict{}try:#通過getopt獲取…

應用開發框架之——根據數據表中的存儲的方法名稱來調用方法

功用一&#xff1a;在框架里面根據存儲在數據表中的方法名來動態調用執行方法。 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 class(TForm) Button1: TButton; procedu…

Spring IOC容器組件注入的幾種方式

整理一下之前Spring的學習筆記&#xff0c;大致有一下幾種Spring注入到容器中的方法: 1&#xff09;、配置在xml的方式。 2&#xff09;、開啟包掃描ComponentScan使用Component&#xff0c;Service&#xff0c;Controller&#xff0c;Repository&#xff08;其實后三個都繼承…

我們是如何拿下Google和Facebook Offer的?

http://posts.careerengine.us/p/57c3a1c1a09633ee7e57803c 大家好&#xff0c;我是小高&#xff0c;CMU CS Master&#xff0c;來Offer第一期學員&#xff0c;2014年初在孫老師的帶領下我在幾個月的時間內進入了Yahoo&#xff0c;并工作了近2年。2016年初&#xff0c;Yahoo工作…

Spring中BeanFactory和FactoryBean的區別

先介紹一下Spring的IOC容器到底是個什么東西&#xff0c;都說是一個控制反轉的容器&#xff0c;將對象的控制權交給IOC容器&#xff0c;其實在看了源代碼之后&#xff0c;就會發現IOC容器只是一個存儲單例的一個ConcurrentHashMap<String, BeanDefinition> BeanDefiniti…

python中數字和字符串可以直接相加_用c語言或者python將文件中特定字符串后面的數字相加...

匿名用戶1級2014-08-31 回答代碼應該不難吧。既然用爬蟲爬下來了&#xff0c;為什么爬取數據的時候沒做處理呢。之前用過Scrapy爬蟲框架&#xff0c;挺好用的&#xff0c;你可研究下。代碼&#xff1a;#!codingutf-8import osimport reimport random# 獲取當前目錄文件列表def …

Spring中Aware的用法以及實現

Aware 在Spring當中有一些內置的對象是未開放給我們使用的&#xff0c;例如Spring的上下文ApplicationContext、環境屬性Environment&#xff0c;BeanFactory等等其他的一些內置對象&#xff0c;而在我們可以通過實現對應的Aware接口去拿到我們想要的一些屬性&#xff0c;一般…

c#字符型轉化為asc_C#字符串和ASCII碼的轉換

//字符轉ASCII碼&#xff1a;public static int Asc(string character){if (character.Length 1){System.Text.ASCIIEncoding asciiEncoding new System.Text.ASCIIEncoding();int intAsciiCode (int)asciiEncoding.GetBytes(character)[0];return (intAsciiCode);}else{thr…

topcoder srm 625 div1

problem1 link 假設第$i$種出現的次數為$n_{i}$&#xff0c;總個數為$m$&#xff0c;那么排列數為$T\frac{m!}{\prod_{i1}^{26}(n_{i}!)}$ 然后計算回文的個數&#xff0c;只需要考慮前一半&#xff0c;得到個數為$R$&#xff0c;那么答案為$\frac{R}{T}$. 為了防止數字太大導致…

Spring的組件賦值以及環境屬性@PropertySource

PropertySource 將指定類路徑下的.properties一些配置加載到Spring當中&#xff0c; 有個跟這個差不多的注解PropertySources Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented public interface PropertySources {PropertySource[] value();} 使用…

python語音識別框架_橫評:五款免費開源的語音識別工具

編者按&#xff1a;本文原作者 Cindi Thompson&#xff0c;美國德克薩斯大學奧斯汀分校(University of Texas at Austin)計算機科學博士&#xff0c;數據科學咨詢公司硅谷數據科學(Silicon Valley Data Science&#xff0c;SVDS)首席科學家&#xff0c;在機器學習、自然語言處理…