concurrenthashmap源码解析(Java7、Java8)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

concurrenthashmap源码解析(Java1.7)

    使用与获取全局信息的方法并不频繁的时候

    01.在 ConcurrentHashMap 中,不允许用 null 作为键和值。

    02.ConcurrentHashMap 使用分段锁(减少锁粒度)技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。


1.源码结构(从上往下)

重要字段

image.png


字段

image.png


ConcurrentHashMap列表条目。请注意,这从来没有导出

image.png


hash函数,段定义

image.png


构造函数

image.png


常用的方法:

image.png


迭代器支持

image.png


序列化支持

image.png


2.数据结构

默认情况下分为16个段。

图片6.png 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static  final  class  HashEntry<K,V> {
         final  int  hash;
         final  K key;
         volatile  V value;
         volatile  HashEntry<K,V> next;
 
         HashEntry( int  hash, K key, V value, HashEntry<K,V> next) {
             this .hash = hash;
             this .key = key;
             this .value = value;
             this .next = next;
         }
 
         /**
          * Sets next field with volatile write semantics.  (See above
          * about use of putOrderedObject.)
          */
         final  void  setNext(HashEntry<K,V> n) {
             UNSAFE.putOrderedObject( this , nextOffset, n);
         }
 
         // Unsafe mechanics
         static  final  sun.misc.Unsafe UNSAFE;
         static  final  long  nextOffset;
         static  {
             try  {
                 UNSAFE = sun.misc.Unsafe.getUnsafe();
                 Class k = HashEntry. class ;
                 nextOffset = UNSAFE.objectFieldOffset
                     (k.getDeclaredField( "next" ));
             catch  (Exception e) {
                 throw  new  Error(e);
             }
         }
     }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
static  final  class  Segment<K,V>  extends  ReentrantLock  implements  Serializable {
       
 
         private  static  final  long  serialVersionUID = 2249069246763182397L;
 
         static  final  int  MAX_SCAN_RETRIES =
             Runtime.getRuntime().availableProcessors() >  1  64  1 ;
 
         transient  volatile  HashEntry<K,V>[] table;
 
         transient  int  count;
 
         transient  int  modCount;
 
         transient  int  threshold;
 
         final  float  loadFactor;
 
         Segment( float  lf,  int  threshold, HashEntry<K,V>[] tab) {
             this .loadFactor = lf;
             this .threshold = threshold;
             this .table = tab;
         }
 
         final  V put(K key,  int  hash, V value,  boolean  onlyIfAbsent) {
             HashEntry<K,V> node = tryLock() ?  null  :
                 scanAndLockForPut(key, hash, value);
             V oldValue;
             try  {
                 HashEntry<K,V>[] tab = table;
                 int  index = (tab.length -  1 ) & hash;
                 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;
                     }
                     else  {
                         if  (node !=  null )
                             node.setNext(first);
                         else
                             node =  new  HashEntry<K,V>(hash, key, value, first);
                         int  c = count +  1 ;
                         if  (c > threshold && tab.length < MAXIMUM_CAPACITY)
                             rehash(node);
                         else
                             setEntryAt(tab, index, node);
                         ++modCount;
                         count = c;
                         oldValue =  null ;
                         break ;
                     }
                 }
             finally  {
                 unlock();
             }
             return  oldValue;
         }
 
         /**
          * Doubles size of table and repacks entries, also adding the
          * given node to new table
          */
         @SuppressWarnings ( "unchecked" )
         private  void  rehash(HashEntry<K,V> node) {
             
             HashEntry<K,V>[] oldTable = table;
             int  oldCapacity = oldTable.length;
             int  newCapacity = oldCapacity <<  1 ;
             threshold = ( int )(newCapacity * loadFactor);
             HashEntry<K,V>[] newTable =
                 (HashEntry<K,V>[])  new  HashEntry[newCapacity];
             int  sizeMask = newCapacity -  1 ;
             for  ( int  i =  0 ; i < oldCapacity ; i++) {
                 HashEntry<K,V> e = oldTable[i];
                 if  (e !=  null ) {
                     HashEntry<K,V> next = e.next;
                     int  idx = e.hash & sizeMask;
                     if  (next ==  null )    //  Single node on list
                         newTable[idx] = e;
                     else  // Reuse consecutive sequence at same slot
                         HashEntry<K,V> lastRun = e;
                         int  lastIdx = idx;
                         for  (HashEntry<K,V> last = next;
                              last !=  null ;
                              last = last.next) {
                             int  k = last.hash & sizeMask;
                             if  (k != lastIdx) {
                                 lastIdx = k;
                                 lastRun = last;
                             }
                         }
                         newTable[lastIdx] = lastRun;
                         // Clone remaining nodes
                         for  (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
                             V v = p.value;
                             int  h = p.hash;
                             int  k = h & sizeMask;
                             HashEntry<K,V> n = newTable[k];
                             newTable[k] =  new  HashEntry<K,V>(h, p.key, v, n);
                         }
                     }
                 }
             }
             int  nodeIndex = node.hash & sizeMask;  // add the new node
             node.setNext(newTable[nodeIndex]);
             newTable[nodeIndex] = node;
             table = newTable;
         }
 
         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 ) {
                     if  (e ==  null ) {
                         if  (node ==  null // speculatively create node
                             node =  new  HashEntry<K,V>(hash, key, value,  null );
                         retries =  0 ;
                     }
                     else  if  (key.equals(e.key))
                         retries =  0 ;
                     else
                         e = e.next;
                 }
                 else  if  (++retries > MAX_SCAN_RETRIES) {
                     lock();
                     break ;
                 }
                 else  if  ((retries &  1 ) ==  0  &&
                          (f = entryForHash( this , hash)) != first) {
                     e = first = f;  // re-traverse if entry changed
                     retries = - 1 ;
                 }
             }
             return  node;
         }
 
         private  void  scanAndLock(Object key,  int  hash) {
             // similar to but simpler than scanAndLockForPut
             HashEntry<K,V> first = entryForHash( this , hash);
             HashEntry<K,V> e = first;
             int  retries = - 1 ;
             while  (!tryLock()) {
                 HashEntry<K,V> f;
                 if  (retries <  0 ) {
                     if  (e ==  null  || key.equals(e.key))
                         retries =  0 ;
                     else
                         e = e.next;
                 }
                 else  if  (++retries > MAX_SCAN_RETRIES) {
                     lock();
                     break ;
                 }
                 else  if  ((retries &  1 ) ==  0  &&
                          (f = entryForHash( this , hash)) != first) {
                     e = first = f;
                     retries = - 1 ;
                 }
             }
         }
 
         final  V remove(Object key,  int  hash, Object value) {
             if  (!tryLock())
                 scanAndLock(key, hash);
             V oldValue =  null ;
             try  {
                 HashEntry<K,V>[] tab = table;
                 int  index = (tab.length -  1 ) & hash;
                 HashEntry<K,V> e = entryAt(tab, index);
                 HashEntry<K,V> pred =  null ;
                 while  (e !=  null ) {
                     K k;
                     HashEntry<K,V> next = e.next;
                     if  ((k = e.key) == key ||
                         (e.hash == hash && key.equals(k))) {
                         V v = e.value;
                         if  (value ==  null  || value == v || value.equals(v)) {
                             if  (pred ==  null )
                                 setEntryAt(tab, index, next);
                             else
                                 pred.setNext(next);
                             ++modCount;
                             --count;
                             oldValue = v;
                         }
                         break ;
                     }
                     pred = e;
                     e = next;
                 }
             finally  {
                 unlock();
             }
             return  oldValue;
         }
 
         final  boolean  replace(K key,  int  hash, V oldValue, V newValue) {
             if  (!tryLock())
                 scanAndLock(key, hash);
             boolean  replaced =  false ;
             try  {
                 HashEntry<K,V> e;
                 for  (e = entryForHash( this , hash); e !=  null ; e = e.next) {
                     K k;
                     if  ((k = e.key) == key ||
                         (e.hash == hash && key.equals(k))) {
                         if  (oldValue.equals(e.value)) {
                             e.value = newValue;
                             ++modCount;
                             replaced =  true ;
                         }
                         break ;
                     }
                 }
             finally  {
                 unlock();
             }
             return  replaced;
         }
 
         final  V replace(K key,  int  hash, V value) {
             if  (!tryLock())
                 scanAndLock(key, hash);
             V oldValue =  null ;
             try  {
                 HashEntry<K,V> e;
                 for  (e = entryForHash( this , hash); e !=  null ; e = e.next) {
                     K k;
                     if  ((k = e.key) == key ||
                         (e.hash == hash && key.equals(k))) {
                         oldValue = e.value;
                         e.value = value;
                         ++modCount;
                         break ;
                     }
                 }
             finally  {
                 unlock();
             }
             return  oldValue;
         }
 
         final  void  clear() {
             lock();
             try  {
                 HashEntry<K,V>[] tab = table;
                 for  ( int  i =  0 ; i < tab.length ; i++)
                     setEntryAt(tab, i,  null );
                 ++modCount;
                 count =  0 ;
             finally  {
                 unlock();
             }
         }
     }

3.put方法

流程:

无标题.png

1
2
3
4
5
6
7
8
9
10
11
12
public  V put(K key, V value) {
         Segment<K,V> s;
         if  (value ==  null )
             throw  new  NullPointerException();
         int  hash = hash(key.hashCode());
         int  j = (hash >>> segmentShift) & segmentMask;
//上面两行用于获取段号
         if  ((s = (Segment<K,V>)UNSAFE.getObject           // nonvolatile; recheck
              (segments, (j << SSHIFT) + SBASE)) ==  null //  in ensureSegment
             s = ensureSegment(j); //得到段,将数据插入到段中
         return  s.put(key, hash, value,  false );
     }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
static  final  class  Segment<K,V>  extends  ReentrantLock  implements  Serializable {
 
         final  V put(K key,  int  hash, V value,  boolean  onlyIfAbsent) {
             HashEntry<K,V> node = tryLock() ?  null  //加锁
                 scanAndLockForPut(key, hash, value);
             V oldValue;
             try  {
                 HashEntry<K,V>[] tab = table;
                 int  index = (tab.length -  1 ) & hash;
                 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;
                     }
                     else  {
                         if  (node !=  null )
                             node.setNext(first);
                         else
                             node =  new  HashEntry<K,V>(hash, key, value, first);
                         int  c = count +  1 ;
                         if  (c > threshold && tab.length < MAXIMUM_CAPACITY)
                             rehash(node);
                         else
                             setEntryAt(tab, index, node);
                         ++modCount;
                         count = c;
                         oldValue =  null ;
                         break ;
                     }
                 }
             finally  {
                 unlock();  //解锁
             }
             return  oldValue;
         }
}


1
2
3
4
5
6
7
8
9
10
11
@SuppressWarnings ( "unchecked" )
     static  final  <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab,  int  i) {
         return  (tab ==  null ) ?  null  :
             (HashEntry<K,V>) UNSAFE.getObjectVolatile
             (tab, (( long )i << TSHIFT) + TBASE);
     }
 
     static  final  <K,V>  void  setEntryAt(HashEntry<K,V>[] tab,  int  i,
                                        HashEntry<K,V> e) {
         UNSAFE.putOrderedObject(tab, (( long )i << TSHIFT) + TBASE, e);
     }


04.当系统需要取得全局锁,消耗资源就会比较多。比如size()方法:事实上会先使用无锁的方式求和,如果失败,会先获得所有段的锁再去求和。

图片7.png 

concurrenthashmap源码解析(Java1.8)

暂时先偷懒,参考别人的:http://blog.csdn.net/zly9923218/article/details/51420561




本文转自 叫我北北 51CTO博客,原文链接:http://blog.51cto.com/qinbin/2057787


相关文章
|
7天前
|
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
前言 下面,跟上主播的节奏,马上开始ThreadLocal源码的阅读( ̄▽ ̄)" 内部结构 如下图所示,我们可以知道,每个线程,都有自己的threadLocals字段,指向ThreadLocalMap
244 81
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
|
28天前
|
重学Java基础篇—Java类加载顺序深度解析
本文全面解析Java类的生命周期与加载顺序,涵盖从加载到卸载的七个阶段,并深入探讨初始化阶段的执行规则。通过单类、继承体系的实例分析,明确静态与实例初始化的顺序。同时,列举六种触发初始化的场景及特殊场景处理(如接口初始化)。提供类加载完整流程图与记忆口诀,助于理解复杂初始化逻辑。此外,针对空指针异常等问题提出排查方案,并给出最佳实践建议,帮助开发者优化程序设计、定位BUG及理解框架机制。最后扩展讲解类加载器层次与双亲委派机制,为深入研究奠定基础。
55 0
|
4天前
|
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
因为本文说的是ReentrantLock源码,因此会默认,大家对AQS有基本的了解(比如同步队列、条件队列大概> 长啥样?)。 不懂AQS的小朋友们,你们好呀!也欢迎先看看这篇
51 13
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
|
8天前
|
【源码】【Java并发】【AQS】从ReentrantLock、Semaphore、CutDownLunch、CyclicBarrier看AQS源码
前言 主播觉得,AQS的原理,就是通过这2个队列的协助,实现核心功能,同步队列(CLH队列)和条件队列(Condition队列)。 同步队列(CLH队列) 作用:管理需要获...
53 18
【源码】【Java并发】【AQS】从ReentrantLock、Semaphore、CutDownLunch、CyclicBarrier看AQS源码
|
6天前
|
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
35 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
5天前
|
深入解析java正则表达式
本文深入解析Java正则表达式的应用,从基础概念到实际开发技巧全面展开。正则表达式是一种强大的文本处理工具,广泛应用于格式验证、搜索替换等场景。Java通过`Pattern`和`Matcher`类支持正则表达式,`Pattern.compile()`方法将正则字符串编译为高效模式对象。文章详细介绍了核心类的功能、常用正则语法及实际案例(如邮箱和电话号码验证)。掌握这些内容,可显著提升文本处理能力,满足多种开发需求。
28 1
重学Java基础篇—ThreadLocal深度解析与最佳实践
ThreadLocal 是一种实现线程隔离的机制,为每个线程创建独立变量副本,适用于数据库连接管理、用户会话信息存储等场景。
68 5
重学Java基础篇—类的生命周期深度解析
本文全面解析了Java类的生命周期,涵盖加载、验证、准备、解析、初始化、使用及卸载七个关键阶段。通过分阶段执行机制详解(如加载阶段的触发条件与技术实现),结合方法调用机制、内存回收保护等使用阶段特性,以及卸载条件和特殊场景处理,帮助开发者深入理解JVM运作原理。同时,文章探讨了性能优化建议、典型异常处理及新一代JVM特性(如元空间与模块化系统)。总结中强调安全优先、延迟加载与动态扩展的设计思想,并提供开发建议与进阶方向,助力解决性能调优、内存泄漏排查及框架设计等问题。
43 5
Java机器学习实战:基于DJL框架的手写数字识别全解析
在人工智能蓬勃发展的今天,Python凭借丰富的生态库(如TensorFlow、PyTorch)成为AI开发的首选语言。但Java作为企业级应用的基石,其在生产环境部署、性能优化和工程化方面的优势不容忽视。DJL(Deep Java Library)的出现完美填补了Java在深度学习领域的空白,它提供了一套统一的API,允许开发者无缝对接主流深度学习框架,将AI模型高效部署到Java生态中。本文将通过手写数字识别的完整流程,深入解析DJL框架的核心机制与应用实践。
68 3
|
5天前
|
Java Lambda 表达式:以 Foo 接口为例深入解析
本文深入解析了 Java 8 中 Lambda 表达式的用法及其背后的函数式接口原理,以 `Foo` 接口为例,展示了如何通过简洁的 Lambda 表达式替代传统匿名类实现。文章从 Lambda 基本语法、函数式接口定义到实际应用层层递进,并探讨默认方法与静态方法的扩展性,最后总结常见误区与关键点,助你高效优化代码!
20 0

推荐镜像

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等