concurrenthashmap源码解析(Java7、Java8)

简介:

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


相关文章
|
3天前
|
监控 安全 NoSQL
采用java+springboot+vue.js+uniapp开发的一整套云MES系统源码 MES制造管理系统源码
MES系统是一套具备实时管理能力,建立一个全面的、集成的、稳定的制造物流质量控制体系;对生产线、工艺、人员、品质、效率等多方位的监控、分析、改进,满足精细化、透明化、自动化、实时化、数据化、一体化管理,实现企业柔性化制造管理。
23 3
|
3天前
|
安全 Java 容器
Java一分钟之-并发编程:并发容器(ConcurrentHashMap, CopyOnWriteArrayList)
【5月更文挑战第18天】本文探讨了Java并发编程中的`ConcurrentHashMap`和`CopyOnWriteArrayList`,两者为多线程数据共享提供高效、线程安全的解决方案。`ConcurrentHashMap`采用分段锁策略,而`CopyOnWriteArrayList`适合读多写少的场景。注意,`ConcurrentHashMap`的`forEach`需避免手动同步,且并发修改时可能导致`ConcurrentModificationException`。`CopyOnWriteArrayList`在写操作时会复制数组。理解和正确使用这些特性是优化并发性能的关键。
9 1
|
3天前
|
Linux 网络安全 Windows
网络安全笔记-day8,DHCP部署_dhcp搭建部署,源码解析
网络安全笔记-day8,DHCP部署_dhcp搭建部署,源码解析
|
4天前
|
存储 Java
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
|
4天前
|
数据采集 监控 安全
java数字工厂MES系统全套源码Java+idea+springboot专业为企业提供智能制造MES解决方案
"MES" 指的是制造执行系统(Manufacturing Execution System)。MES在制造业中扮演着至关重要的角色,它是位于企业资源计划(ERP)系统和车间控制系统之间的系统,用于实时收集、管理、分析和报告与制造过程相关的数据。
10 0
|
4天前
|
移动开发 监控 供应链
JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(app、小程序、H5、台后管理端)
一开始接触MES系统,很多人会和博主一样,对MES细节的应用不了解,这样很正常,因为MES系统相对于其他系统来讲应用比较多!
15 1
JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(app、小程序、H5、台后管理端)
|
4天前
|
缓存 算法 Java
数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
|
4天前
|
NoSQL 算法 Java
【redis源码学习】持久化机制,java程序员面试算法宝典pdf
【redis源码学习】持久化机制,java程序员面试算法宝典pdf
|
4天前
HuggingFace Tranformers 源码解析(4)
HuggingFace Tranformers 源码解析
6 0
|
4天前
HuggingFace Tranformers 源码解析(3)
HuggingFace Tranformers 源码解析
7 0

推荐镜像

更多