/
304643e5.html
333 lines (303 loc) · 86.5 KB
/
304643e5.html
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
<!DOCTYPE html><html class="theme-next gemini use-motion" lang="zh-Hans"><head><meta name="generator" content="Hexo 3.8.0"><meta name="google-site-verification" content="SJDgyqjBwkGnbvvQE_jdQKhgkQfRh7WhtS0-E3egoyk"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="theme-color" content="#222"><meta name="viewport" content="width=device-width"><script src="/lib/pace/pace.min.js?v=1.0.2"></script><link href="/lib/pace/pace-theme-minimal.min.css?v=1.0.2" rel="stylesheet"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css"><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4"><link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222"><meta name="keywords" content="Java,线程安全,并发编程,锁机制,同步,synchronized,锁,ConcurrentHashMap,"><link rel="alternate" href="/atom.xml" title="从入门到放弃" type="application/atom+xml"><meta name="description" content="前言上文【从入门到放弃-Java】并发编程-锁-synchronized中,我们介绍了可以使用内置锁synchronized同步类或代码块儿,到达线程安全的目的。 jdk帮我们把常用的一些模块封装成同步容器,如Vector、Hashtable、Collections.synchronizedXxx等。实现方式主要是将常用的容器类加了Synchronized同步。但我们知道,synchronized"><meta name="keywords" content="Java,线程安全,并发编程,锁机制,同步,synchronized,锁,ConcurrentHashMap"><meta property="og:type" content="article"><meta property="og:title" content="【从入门到放弃-Java】并发编程-JUC-ConcurrentHashMap"><meta property="og:url" content="https://nc2era.com/304643e5.html"><meta property="og:site_name" content="从入门到放弃"><meta property="og:description" content="前言上文【从入门到放弃-Java】并发编程-锁-synchronized中,我们介绍了可以使用内置锁synchronized同步类或代码块儿,到达线程安全的目的。 jdk帮我们把常用的一些模块封装成同步容器,如Vector、Hashtable、Collections.synchronizedXxx等。实现方式主要是将常用的容器类加了Synchronized同步。但我们知道,synchronized"><meta property="og:locale" content="zh-Hans"><meta property="og:updated_time" content="2019-07-28T07:57:31.891Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="【从入门到放弃-Java】并发编程-JUC-ConcurrentHashMap"><meta name="twitter:description" content="前言上文【从入门到放弃-Java】并发编程-锁-synchronized中,我们介绍了可以使用内置锁synchronized同步类或代码块儿,到达线程安全的目的。 jdk帮我们把常用的一些模块封装成同步容器,如Vector、Hashtable、Collections.synchronizedXxx等。实现方式主要是将常用的容器类加了Synchronized同步。但我们知道,synchronized"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",scheme:"Gemini",version:"5.1.4",sidebar:{position:"left",display:"post",offset:12,b2t:!1,scrollpercent:!1,onmobile:!1},fancybox:!0,tabs:!0,motion:{enable:!0,async:!1,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},duoshuo:{userId:"0",author:"博主"},algolia:{applicationID:"",apiKey:"",indexName:"",hits:{per_page:10},labels:{input_placeholder:"Search for Posts",hits_empty:"We didn't find any results for the search: ${query}",hits_stats:"${hits} results found in ${time} ms"}}}</script><link rel="canonical" href="https://nc2era.com/304643e5.html"><title>【从入门到放弃-Java】并发编程-JUC-ConcurrentHashMap | 从入门到放弃</title><script type="text/javascript">var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?7f0f37da8af427b455867492ca709c92";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script></head><body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans"><div class="container sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><div class="custom-logo-site-title"><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">从入门到放弃</span><span class="logo-line-after"><i></i></span></a></div><h1 class="site-subtitle" itemprop="description">从入门到放弃-程序员进阶之路</h1></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i><br> 首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br> 标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br> 分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> 归档</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> 搜索</a></li></ul><div class="site-search"><div class="popup search-popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i></span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"> <input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><div class="post-block"><link itemprop="mainEntityOfPage" href="https://nc2era.com/304643e5.html"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="AloofJr"><meta itemprop="description" content><meta itemprop="image" content="/images/avatar.gif"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="从入门到放弃"></span><header class="post-header"><h2 class="post-title" itemprop="name headline">【从入门到放弃-Java】并发编程-JUC-ConcurrentHashMap</h2><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <span class="post-meta-item-text">发表于</span> <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-07-23T22:05:48+08:00">2019-07-23</time></span> <span class="post-comments-count"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-comment-o"></i></span><a href="/304643e5.html#comments" itemprop="discussionUrl"><span class="post-comments-count valine-comment-count" data-xid="/304643e5.html" itemprop="commentCount"></span></a></span> <span class="post-meta-divider">|</span><span class="page-pv"><i class="fa fa-file-o"></i> 浏览<span class="busuanzi-value" id="busuanzi_value_page_pv"></span></span><div class="post-wordcount"><span class="post-meta-item-icon"><i class="fa fa-file-word-o"></i></span> <span class="post-meta-item-text">字数统计:</span> <span title="字数统计">3.3k</span> <span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-clock-o"></i></span> <span class="post-meta-item-text">阅读时长 ≈</span> <span title="阅读时长">16</span></div></div></header><div class="post-body" itemprop="articleBody"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>上文<a href="https://nc2era.com/3b701acb.html">【从入门到放弃-Java】并发编程-锁-synchronized</a>中,我们介绍了可以使用内置锁synchronized同步类或代码块儿,到达线程安全的目的。</p><p>jdk帮我们把常用的一些模块封装成同步容器,如Vector、Hashtable、Collections.synchronizedXxx等。实现方式主要是将常用的容器类加了Synchronized同步。但我们知道,synchronized的频繁使用及竞争较为激烈时,对性能的影响比较大。</p><p>jdk1.5之后为我们提供了多种并发容器类,来提升同步容器的性能,这些类主要在java.util.concurrent包(简称juc,包内还有很多其它的并发工具类)中。我们本文先来学习下最常用的并发容器-ConcurrentHashMap。</p><h2 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h2><h3 id="put"><a href="#put" class="headerlink" title="put"></a>put</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Maps the specified key to the specified value in this table.</span></span><br><span class="line"><span class="comment"> * Neither the key nor the value can be null.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <p>The value can be retrieved by calling the {<span class="doctag">@code</span> get} method</span></span><br><span class="line"><span class="comment"> * with a key that is equal to the original key.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> key key with which the specified value is to be associated</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value value to be associated with the specified key</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> the previous value associated with {<span class="doctag">@code</span> key}, or</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@code</span> null} if there was no mapping for {<span class="doctag">@code</span> key}</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NullPointerException if the specified key or value is null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// key和value都不能是null</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> putVal(key, value, <span class="keyword">false</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/** Implementation for put and putIfAbsent */</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(K key, V value, <span class="keyword">boolean</span> onlyIfAbsent)</span> </span>{</span><br><span class="line"> <span class="comment">//如果key或者value是null则立即抛出空指针异常</span></span><br><span class="line"> <span class="keyword">if</span> (key == <span class="keyword">null</span> || value == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//求hash值,将哈希的高位扩展到低位,并将高位强制为0。主要是为了减少hash冲突。</span></span><br><span class="line"> <span class="keyword">int</span> hash = spread(key.hashCode());</span><br><span class="line"> <span class="keyword">int</span> binCount = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//Node是Map.Entry的实现类,存放key、value。但key、value都不能是null。table的个数是2的n次方</span></span><br><span class="line"> <span class="keyword">for</span> (Node<K,V>[] tab = table;;) {</span><br><span class="line"> Node<K,V> f; <span class="keyword">int</span> n, i, fh; K fk; V fv;</span><br><span class="line"> <span class="comment">//Node会延迟初始化、即在第一次插入数据的时候进行初始化</span></span><br><span class="line"> <span class="keyword">if</span> (tab == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line"> tab = initTable();</span><br><span class="line"> <span class="comment">//以原子的方式获取Node数组n-1位置的node,如果未null,尝试插入新值</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i = (n - <span class="number">1</span>) & hash)) == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="comment">//使用cas的方式设置新node的key、value值</span></span><br><span class="line"> <span class="keyword">if</span> (casTabAt(tab, i, <span class="keyword">null</span>, <span class="keyword">new</span> Node<K,V>(hash, key, value)))</span><br><span class="line"> <span class="keyword">break</span>; <span class="comment">// no lock when adding to empty bin</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果Node是一个ForwardingNode,即有其它线程在扩容,则一起进行扩容操作</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</span><br><span class="line"> <span class="comment">//如果当前正在扩容,则当前线程加入一起帮助扩容。</span></span><br><span class="line"> tab = helpTransfer(tab, f);</span><br><span class="line"> <span class="comment">//当使用putIfAbsent时,如果map中存在key,则返回对应的value</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (onlyIfAbsent <span class="comment">// check first node without acquiring lock</span></span><br><span class="line"> && fh == hash</span><br><span class="line"> && ((fk = f.key) == key || (fk != <span class="keyword">null</span> && key.equals(fk)))</span><br><span class="line"> && (fv = f.val) != <span class="keyword">null</span>)</span><br><span class="line"> <span class="keyword">return</span> fv;</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> V oldVal = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">/** </span></span><br><span class="line"><span class="comment"> * currentHashMap在JDK1.8中使用synchronized对需要修改的Node加锁同步,替代了JDK1.7及之前版本采用分段锁的方式。两种方式对比:</span></span><br><span class="line"><span class="comment"> * 1、1.7采用数组+Segment+分段锁的方式实现,分段锁及将几个map分为多个类似hashmap的结构,内部是多个Entry链表数组。加锁时,使用ReentrantLock对访问的Segment加锁,其它Segment可以正常操作。缺点是寻找节点需要两次hash,一次找到Segment,一次找到Entry链表的头部。</span></span><br><span class="line"><span class="comment"> * 2、1.8采用数组+链表或红黑树的方式实现。使用Node替代了Segment,采用了CAS及synchronized进行同步。当Node链表的长度大于阙值(默认为8)时,会将链表转化为红黑树,提升查找性能。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">//通过synchronized的方式,对当前Node进行加锁操作。</span></span><br><span class="line"> <span class="keyword">synchronized</span> (f) {</span><br><span class="line"> <span class="comment">//判断f节点是否已被其它线程修改</span></span><br><span class="line"> <span class="keyword">if</span> (tabAt(tab, i) == f) {</span><br><span class="line"> <span class="comment">//如果当前Node还是链表结构时</span></span><br><span class="line"> <span class="keyword">if</span> (fh >= <span class="number">0</span>) {</span><br><span class="line"> binCount = <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//遍历Node链表,设置value</span></span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> e = f;; ++binCount) {</span><br><span class="line"> K ek;</span><br><span class="line"> <span class="comment">//如果当前节点的key与我们要设置的key相等时,则将值设置为value。</span></span><br><span class="line"> <span class="keyword">if</span> (e.hash == hash &&</span><br><span class="line"> ((ek = e.key) == key ||</span><br><span class="line"> (ek != <span class="keyword">null</span> && key.equals(ek)))) {</span><br><span class="line"> oldVal = e.val;</span><br><span class="line"> <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line"> e.val = value;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> Node<K,V> pred = e;</span><br><span class="line"> <span class="comment">//设置e为Node链表中的下一个元素,继续判断key是否相等,直到找到相等的key设置值。但如果链表中没有相等的key时,则在链表尾部新增一个元素,并设置值。</span></span><br><span class="line"> <span class="keyword">if</span> ((e = e.next) == <span class="keyword">null</span>) {</span><br><span class="line"> pred.next = <span class="keyword">new</span> Node<K,V>(hash, key, value);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果当前Node为红黑树结构时</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) {</span><br><span class="line"> Node<K,V> p;</span><br><span class="line"> binCount = <span class="number">2</span>;</span><br><span class="line"> <span class="comment">//设置值</span></span><br><span class="line"> <span class="keyword">if</span> ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,</span><br><span class="line"> value)) != <span class="keyword">null</span>) {</span><br><span class="line"> oldVal = p.val;</span><br><span class="line"> <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line"> p.val = value;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> ReservationNode)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Recursive update"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果Node链表的长度大于8时,判断是链表结构扩容,或者转为红黑树结构</span></span><br><span class="line"> <span class="keyword">if</span> (binCount != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (binCount >= TREEIFY_THRESHOLD)</span><br><span class="line"> treeifyBin(tab, i);</span><br><span class="line"> <span class="keyword">if</span> (oldVal != <span class="keyword">null</span>)</span><br><span class="line"> <span class="keyword">return</span> oldVal;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> addCount(<span class="number">1L</span>, binCount);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Copies all of the mappings from the specified map to this one.</span></span><br><span class="line"><span class="comment"> * These mappings replace any mappings that this map had for any of the</span></span><br><span class="line"><span class="comment"> * keys currently in the specified map.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> m mappings to be stored in this map</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">putAll</span><span class="params">(Map<? extends K, ? extends V> m)</span> </span>{</span><br><span class="line"> tryPresize(m.size());</span><br><span class="line"> <span class="keyword">for</span> (Map.Entry<? extends K, ? extends V> e : m.entrySet())</span><br><span class="line"> putVal(e.getKey(), e.getValue(), <span class="keyword">false</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="get"><a href="#get" class="headerlink" title="get"></a>get</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Returns the value to which the specified key is mapped,</span></span><br><span class="line"><span class="comment"> * or {<span class="doctag">@code</span> null} if this map contains no mapping for the key.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <p>More formally, if this map contains a mapping from a key</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@code</span> k} to a value {<span class="doctag">@code</span> v} such that {<span class="doctag">@code</span> key.equals(k)},</span></span><br><span class="line"><span class="comment"> * then this method returns {<span class="doctag">@code</span> v}; otherwise it returns</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@code</span> null}. (There can be at most one such mapping.)</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NullPointerException if the specified key is null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>{</span><br><span class="line"> Node<K,V>[] tab; Node<K,V> e, p; <span class="keyword">int</span> n, eh; K ek;</span><br><span class="line"> <span class="comment">//获取hash值</span></span><br><span class="line"> <span class="keyword">int</span> h = spread(key.hashCode());</span><br><span class="line"> <span class="keyword">if</span> ((tab = table) != <span class="keyword">null</span> && (n = tab.length) > <span class="number">0</span> &&</span><br><span class="line"> (e = tabAt(tab, (n - <span class="number">1</span>) & h)) != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="comment">//如果获取到的Node的hash值和key的相等,则说明是链表。</span></span><br><span class="line"> <span class="keyword">if</span> ((eh = e.hash) == h) {</span><br><span class="line"> <span class="keyword">if</span> ((ek = e.key) == key || (ek != <span class="keyword">null</span> && key.equals(ek)))</span><br><span class="line"> <span class="keyword">return</span> e.val;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果获取Node的hash值小于0则说明是非链式结构</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (eh < <span class="number">0</span>)</span><br><span class="line"> <span class="comment">//不断查找Node的下一个节点,知道找到为止</span></span><br><span class="line"> <span class="keyword">return</span> (p = e.find(h, key)) != <span class="keyword">null</span> ? p.val : <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//不断查找Node的下一个节点,直到找到为止(感觉和find重复了。最外层的if中只需要一个Node::find方法就能搞定。知道原因的大神请指正)</span></span><br><span class="line"> <span class="keyword">while</span> ((e = e.next) != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">if</span> (e.hash == h &&</span><br><span class="line"> ((ek = e.key) == key || (ek != <span class="keyword">null</span> && key.equals(ek))))</span><br><span class="line"> <span class="keyword">return</span> e.val;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="treeifyBin"><a href="#treeifyBin" class="headerlink" title="treeifyBin"></a>treeifyBin</h3><p>扩容或将结构转为红黑树<br></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">treeifyBin</span><span class="params">(Node<K,V>[] tab, <span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> Node<K,V> b; <span class="keyword">int</span> n;</span><br><span class="line"> <span class="keyword">if</span> (tab != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="comment">//如果当前Node数组小于64则扩容,大于64时则转换为红黑树结构</span></span><br><span class="line"> <span class="keyword">if</span> ((n = tab.length) < MIN_TREEIFY_CAPACITY)</span><br><span class="line"> <span class="comment">//rehash:resize</span></span><br><span class="line"> tryPresize(n << <span class="number">1</span>);</span><br><span class="line"> <span class="comment">//如果是链表结构则转换为红黑树结构</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((b = tabAt(tab, index)) != <span class="keyword">null</span> && b.hash >= <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">synchronized</span> (b) {</span><br><span class="line"> <span class="keyword">if</span> (tabAt(tab, index) == b) {</span><br><span class="line"> TreeNode<K,V> hd = <span class="keyword">null</span>, tl = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> e = b; e != <span class="keyword">null</span>; e = e.next) {</span><br><span class="line"> <span class="comment">//创建树节点,加入红黑树中</span></span><br><span class="line"> TreeNode<K,V> p =</span><br><span class="line"> <span class="keyword">new</span> TreeNode<K,V>(e.hash, e.key, e.val,</span><br><span class="line"> <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">if</span> ((p.prev = tl) == <span class="keyword">null</span>)</span><br><span class="line"> hd = p;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> tl.next = p;</span><br><span class="line"> tl = p;</span><br><span class="line"> }</span><br><span class="line"> setTabAt(tab, index, <span class="keyword">new</span> TreeBin<K,V>(hd));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><h3 id="tryPresize"><a href="#tryPresize" class="headerlink" title="tryPresize"></a>tryPresize</h3><p>扩容操作<br></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Tries to presize table to accommodate the given number of elements.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> size number of elements (doesn't need to be perfectly accurate)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">tryPresize</span><span class="params">(<span class="keyword">int</span> size)</span> </span>{</span><br><span class="line"> <span class="comment">//size在传入前已经翻倍,这里会再次调整,变为为:大于(1.5 * oldSize + 1)的2的幂,且小于MAXIMUM_CAPACITY的大小</span></span><br><span class="line"> <span class="keyword">int</span> c = (size >= (MAXIMUM_CAPACITY >>> <span class="number">1</span>)) ? MAXIMUM_CAPACITY :</span><br><span class="line"> tableSizeFor(size + (size >>> <span class="number">1</span>) + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">int</span> sc;</span><br><span class="line"> <span class="comment">//当sizeCtl小于等于0时。说明已有线程在初始化或者rehash了</span></span><br><span class="line"> <span class="keyword">while</span> ((sc = sizeCtl) >= <span class="number">0</span>) {</span><br><span class="line"> Node<K,V>[] tab = table; <span class="keyword">int</span> n;</span><br><span class="line"> 如果table是空,即未初始化的话,进行初始化。</span><br><span class="line"> <span class="keyword">if</span> (tab == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>) {</span><br><span class="line"> n = (sc > c) ? sc : c;</span><br><span class="line"> <span class="keyword">if</span> (U.compareAndSetInt(<span class="keyword">this</span>, SIZECTL, sc, -<span class="number">1</span>)) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span> (table == tab) {</span><br><span class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line"> Node<K,V>[] nt = (Node<K,V>[])<span class="keyword">new</span> Node<?,?>[n];</span><br><span class="line"> table = nt;</span><br><span class="line"> <span class="comment">//sc = n - n / 4 = 0.75,在final中,将sizeCtl设置为当前大小的0.75倍。大于这个阙值时,会再次进行扩容。</span></span><br><span class="line"> sc = n - (n >>> <span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> sizeCtl = sc;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (c <= sc || n >= MAXIMUM_CAPACITY)</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="comment">//如果还未开始迁移</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (tab == table) {</span><br><span class="line"> <span class="keyword">int</span> rs = resizeStamp(n);</span><br><span class="line"> <span class="keyword">if</span> (U.compareAndSetInt(<span class="keyword">this</span>, SIZECTL, sc,</span><br><span class="line"> (rs << RESIZE_STAMP_SHIFT) + <span class="number">2</span>))</span><br><span class="line"> <span class="comment">// 开始迁移</span></span><br><span class="line"> transfer(tab, <span class="keyword">null</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><h3 id="transfer"><a href="#transfer" class="headerlink" title="transfer"></a>transfer</h3><p>将Node迁移至新的table中<br></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Moves and/or copies the nodes in each bin to new table. See</span></span><br><span class="line"><span class="comment"> * above for explanation.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Node<K,V>[] tab, Node<K,V>[] nextTab)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> n = tab.length, stride;</span><br><span class="line"> <span class="comment">//设置线程迁移数据的步长,单核步长为n,多核为(n >>> 3) / NCPU, 最小为16</span></span><br><span class="line"> <span class="keyword">if</span> ((stride = (NCPU > <span class="number">1</span>) ? (n >>> <span class="number">3</span>) / NCPU : n) < MIN_TRANSFER_STRIDE)</span><br><span class="line"> stride = MIN_TRANSFER_STRIDE; <span class="comment">// subdivide range</span></span><br><span class="line"> <span class="comment">//如果要迁移的table还未初始化,则进行初始化动作</span></span><br><span class="line"> <span class="keyword">if</span> (nextTab == <span class="keyword">null</span>) { <span class="comment">// initiating</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line"> Node<K,V>[] nt = (Node<K,V>[])<span class="keyword">new</span> Node<?,?>[n << <span class="number">1</span>];</span><br><span class="line"> nextTab = nt;</span><br><span class="line"> } <span class="keyword">catch</span> (Throwable ex) { <span class="comment">// try to cope with OOME</span></span><br><span class="line"> sizeCtl = Integer.MAX_VALUE;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> nextTable = nextTab;</span><br><span class="line"> transferIndex = n;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> nextn = nextTab.length;</span><br><span class="line"> <span class="comment">//开始迁移,为要迁移的Node创建一个ForwardingNode节点。key和value都是null,hashcode为MOVED,nextTable指向新的table</span></span><br><span class="line"> ForwardingNode<K,V> fwd = <span class="keyword">new</span> ForwardingNode<K,V>(nextTab);</span><br><span class="line"> <span class="comment">//表示一个节点已被迁移完毕,可以迁移下一个了。</span></span><br><span class="line"> <span class="keyword">boolean</span> advance = <span class="keyword">true</span>;</span><br><span class="line"> <span class="comment">//迁移过程是否完毕。</span></span><br><span class="line"> <span class="keyword">boolean</span> finishing = <span class="keyword">false</span>; <span class="comment">// to ensure sweep before committing nextTab</span></span><br><span class="line"> <span class="comment">//i是迁移的起始位置,bound是迁移的末尾。</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, bound = <span class="number">0</span>;;) {</span><br><span class="line"> Node<K,V> f; <span class="keyword">int</span> fh;</span><br><span class="line"> <span class="keyword">while</span> (advance) {</span><br><span class="line"> <span class="keyword">int</span> nextIndex, nextBound;</span><br><span class="line"> <span class="comment">//其实位置大于结束位置,说明已经迁移完毕</span></span><br><span class="line"> <span class="keyword">if</span> (--i >= bound || finishing)</span><br><span class="line"> advance = <span class="keyword">false</span>;</span><br><span class="line"> <span class="comment">//如果transferIndex小于等于0,则说明节点都已有线程在迁移了</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((nextIndex = transferIndex) <= <span class="number">0</span>) {</span><br><span class="line"> i = -<span class="number">1</span>;</span><br><span class="line"> advance = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSetInt</span><br><span class="line"> (<span class="keyword">this</span>, TRANSFERINDEX, nextIndex,</span><br><span class="line"> nextBound = (nextIndex > stride ?</span><br><span class="line"> nextIndex - stride : <span class="number">0</span>))) {</span><br><span class="line"> bound = nextBound;</span><br><span class="line"> i = nextIndex - <span class="number">1</span>;</span><br><span class="line"> advance = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//迁移结束</span></span><br><span class="line"> <span class="keyword">if</span> (i < <span class="number">0</span> || i >= n || i + n >= nextn) {</span><br><span class="line"> <span class="keyword">int</span> sc;</span><br><span class="line"> <span class="comment">//迁移完毕后,将新的table赋值给table成员变量,修改sizeCtl完成迁移</span></span><br><span class="line"> <span class="keyword">if</span> (finishing) {</span><br><span class="line"> nextTable = <span class="keyword">null</span>;</span><br><span class="line"> table = nextTab;</span><br><span class="line"> sizeCtl = (n << <span class="number">1</span>) - (n >>> <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (U.compareAndSetInt(<span class="keyword">this</span>, SIZECTL, sc = sizeCtl, sc - <span class="number">1</span>)) {</span><br><span class="line"> <span class="comment">//不相等说明还有线程没迁移完毕</span></span><br><span class="line"> <span class="keyword">if</span> ((sc - <span class="number">2</span>) != resizeStamp(n) << RESIZE_STAMP_SHIFT)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> <span class="comment">//所有线程迁移完毕后,设置finishing为完成。</span></span><br><span class="line"> finishing = advance = <span class="keyword">true</span>;</span><br><span class="line"> i = n; <span class="comment">// recheck before commit</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果tab[i] = null,设置为fwd</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i)) == <span class="keyword">null</span>)</span><br><span class="line"> advance = casTabAt(tab, i, <span class="keyword">null</span>, fwd);</span><br><span class="line"> <span class="comment">//如果当前节点已经迁移,则处理下一个节点</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</span><br><span class="line"> advance = <span class="keyword">true</span>; <span class="comment">// already processed</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//加锁同步处理</span></span><br><span class="line"> <span class="keyword">synchronized</span> (f) {</span><br><span class="line"> <span class="comment">//验证下是否已经被其它线程处理</span></span><br><span class="line"> <span class="keyword">if</span> (tabAt(tab, i) == f) {</span><br><span class="line"> Node<K,V> ln, hn;</span><br><span class="line"> <span class="comment">//如果是链表结构</span></span><br><span class="line"> <span class="keyword">if</span> (fh >= <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">//按照Node中元素hash值的第log(2)(n)位,记为runBit,是0或1将Node链表分为两个新的链表。</span></span><br><span class="line"> <span class="keyword">int</span> runBit = fh & n;</span><br><span class="line"> Node<K,V> lastRun = f;</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> p = f.next; p != <span class="keyword">null</span>; p = p.next) {</span><br><span class="line"> <span class="keyword">int</span> b = p.hash & n;</span><br><span class="line"> <span class="keyword">if</span> (b != runBit) {</span><br><span class="line"> runBit = b;</span><br><span class="line"> lastRun = p;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将runBit位为0的链表记为ln,为1的设为hn。这里是标记最后一个不一致的节点,lastRun后节点的runBit都一样,因此不用新修改节点,减少消耗</span></span><br><span class="line"> <span class="keyword">if</span> (runBit == <span class="number">0</span>) {</span><br><span class="line"> ln = lastRun;</span><br><span class="line"> hn = <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> hn = lastRun;</span><br><span class="line"> ln = <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> p = f; p != lastRun; p = p.next) {</span><br><span class="line"> <span class="keyword">int</span> ph = p.hash; K pk = p.key; V pv = p.val;</span><br><span class="line"> <span class="comment">//将runBit位为0的链表记为ln,为1的设为hn。 </span></span><br><span class="line"> <span class="keyword">if</span> ((ph & n) == <span class="number">0</span>)</span><br><span class="line"> ln = <span class="keyword">new</span> Node<K,V>(ph, pk, pv, ln);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> hn = <span class="keyword">new</span> Node<K,V>(ph, pk, pv, hn);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将分开的两个节点设置为table的i和i+n位。</span></span><br><span class="line"> setTabAt(nextTab, i, ln);</span><br><span class="line"> setTabAt(nextTab, i + n, hn);</span><br><span class="line"> setTabAt(tab, i, fwd);</span><br><span class="line"> advance = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果是红黑树结构</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) {</span><br><span class="line"> TreeBin<K,V> t = (TreeBin<K,V>)f;</span><br><span class="line"> <span class="comment">//按照Node中元素hash值的第log(2)(n)位,记为runBit,是0或1将Node红黑树分为两颗树。</span></span><br><span class="line"> TreeNode<K,V> lo = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</span><br><span class="line"> TreeNode<K,V> hi = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">int</span> lc = <span class="number">0</span>, hc = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> e = t.first; e != <span class="keyword">null</span>; e = e.next) {</span><br><span class="line"> <span class="keyword">int</span> h = e.hash;</span><br><span class="line"> TreeNode<K,V> p = <span class="keyword">new</span> TreeNode<K,V></span><br><span class="line"> (h, e.key, e.val, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">if</span> ((h & n) == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> ((p.prev = loTail) == <span class="keyword">null</span>)</span><br><span class="line"> lo = p;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> loTail.next = p;</span><br><span class="line"> loTail = p;</span><br><span class="line"> ++lc;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> ((p.prev = hiTail) == <span class="keyword">null</span>)</span><br><span class="line"> hi = p;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> hiTail.next = p;</span><br><span class="line"> hiTail = p;</span><br><span class="line"> ++hc;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :</span><br><span class="line"> (hc != <span class="number">0</span>) ? <span class="keyword">new</span> TreeBin<K,V>(lo) : t;</span><br><span class="line"> hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :</span><br><span class="line"> (lc != <span class="number">0</span>) ? <span class="keyword">new</span> TreeBin<K,V>(hi) : t;</span><br><span class="line"> setTabAt(nextTab, i, ln);</span><br><span class="line"> setTabAt(nextTab, i + n, hn);</span><br><span class="line"> setTabAt(tab, i, fwd);</span><br><span class="line"> advance = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> ReservationNode)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Recursive update"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文分析了currentHashMap是如何实现线程安全并提升性能的、如何扩容、JDK1.7和1.8实现方式的区别等</p><ul><li>分Node加synchronize锁,不影响其它node的读写</li><li>Node节点hash冲突的元素数量少于8时,使用链表结构,大于等于8时,转换为红黑树结构提升查找性能</li><li>扩容时,会将table的长度扩大为大于(1.5 * oldSize + 1)的2的幂大小,并将每个Node根据log(2)(n)位是0或1,分为两个Node,放在新table的i和i+n的位置</li><li>JDK1.8将原currentHashMap使用数组+segment+ReentrantLock的方式改为数组+Node+CAS+synchronized的方式。减少了hash次数并采用cas和红黑树等多种优化提升性能</li></ul></div><footer class="post-footer"><div class="post-tags"> <a href="/tags/Java/" rel="tag"># Java</a> <a href="/tags/线程安全/" rel="tag"># 线程安全</a> <a href="/tags/并发编程/" rel="tag"># 并发编程</a> <a href="/tags/锁机制/" rel="tag"># 锁机制</a> <a href="/tags/同步/" rel="tag"># 同步</a> <a href="/tags/synchronized/" rel="tag"># synchronized</a> <a href="/tags/锁/" rel="tag"># 锁</a> <a href="/tags/ConcurrentHashMap/" rel="tag"># ConcurrentHashMap</a></div><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/3b701acb.html" rel="next" title="【从入门到放弃-Java】并发编程-锁-synchronized"><i class="fa fa-chevron-left"></i> 【从入门到放弃-Java】并发编程-锁-synchronized</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"> <a href="/4f4e5516.html" rel="prev" title="【从入门到放弃-Java】并发编程-JUC-CopyOnWriteArrayList">【从入门到放弃-Java】并发编程-JUC-CopyOnWriteArrayList<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-spread"></div></div></div><div class="comments" id="comments"></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> 文章目录</li><li class="sidebar-nav-overview" data-target="site-overview-wrap"> 站点概览</li></ul><section class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"><p class="site-author-name" itemprop="name">AloofJr</p><p class="site-description motion-element" itemprop="description"></p></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives/"><span class="site-state-item-count">47</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories/index.html"><span class="site-state-item-count">1</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags/index.html"><span class="site-state-item-count">50</span> <span class="site-state-item-name">标签</span></a></div></nav><div class="feed-link motion-element"><a href="/atom.xml" rel="alternate"><i class="fa fa-rss"></i> RSS</a></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/AloofJr" target="_blank" title="GitHub" rel="external nofollow noopener noreferrer"><i class="fa fa-fw fa-github"></i> GitHub</a></span></div></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#前言"><span class="nav-number">1.</span> <span class="nav-text">前言</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ConcurrentHashMap"><span class="nav-number">2.</span> <span class="nav-text">ConcurrentHashMap</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#put"><span class="nav-number">2.1.</span> <span class="nav-text">put</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#get"><span class="nav-number">2.2.</span> <span class="nav-text">get</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#treeifyBin"><span class="nav-number">2.3.</span> <span class="nav-text">treeifyBin</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#tryPresize"><span class="nav-number">2.4.</span> <span class="nav-text">tryPresize</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#transfer"><span class="nav-number">2.5.</span> <span class="nav-text">transfer</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#总结"><span class="nav-number">3.</span> <span class="nav-text">总结</span></a></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright">© <span itemprop="copyrightYear">2020</span><span class="with-love"><i class="fa fa-user"></i></span> <span class="author" itemprop="copyrightHolder">AloofJr</span> <span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-area-chart"></i></span> <span class="post-meta-item-text">Site words total count:</span> <span title="Site words total count">79.4k</span></div><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><div class="busuanzi-count"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="site-uv"><i class="fa fa-user"></i><span class="busuanzi-value" id="busuanzi_value_site_uv"></span></span><span class="site-pv"><i class="fa fa-eye"></i><span class="busuanzi-value" id="busuanzi_value_site_pv"></span></span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script><script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script><script src="//unpkg.com/valine/dist/Valine.min.js"></script><script type="text/javascript">
var GUEST = ['nick','mail','link'];
var guest = 'nick,mail';
guest = guest.split(',').filter(item=>{
return GUEST.indexOf(item)>-1;
});
new Valine({
el: '#comments' ,
verify: true,
notify: true,
appId: 'AtTFeTEomwo5ADIQTR8YFRcL-gzGzoHsz',
appKey: 'vKO8qLF0TzyALMhWTxgQ7GPh',
placeholder: '欢迎各路大神交流指正!',
avatar:'mm',
guest_info:guest,
pageSize:'10' || 10,
});
</script><script type="text/javascript">
// Popup Window;
var isfetched = false;
var isXml = true;
// Search DB path;
var search_path = "search.xml";
if (search_path.length === 0) {
search_path = "search.xml";
} else if (/json$/i.test(search_path)) {
isXml = false;
}
var path = "/" + search_path;
// monitor main search box;
var onPopupClose = function (e) {
$('.popup').hide();
$('#local-search-input').val('');
$('.search-result-list').remove();
$('#no-result').remove();
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
}
function proceedsearch() {
$("body")
.append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
.css('overflow', 'hidden');
$('.search-popup-overlay').click(onPopupClose);
$('.popup').toggle();
var $localSearchInput = $('#local-search-input');
$localSearchInput.attr("autocapitalize", "none");
$localSearchInput.attr("autocorrect", "off");
$localSearchInput.focus();
}
// search function;
var searchFunc = function(path, search_id, content_id) {
'use strict';
// start loading animation
$("body")
.append('<div class="search-popup-overlay local-search-pop-overlay">' +
'<div id="search-loading-icon">' +
'<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
'</div>' +
'</div>')
.css('overflow', 'hidden');
$("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');
$.ajax({
url: path,
dataType: isXml ? "xml" : "json",
async: true,
success: function(res) {
// get the contents from search data
isfetched = true;
$('.popup').detach().appendTo('.header-inner');
var datas = isXml ? $("entry", res).map(function() {
return {
title: $("title", this).text(),
content: $("content",this).text(),
url: $("url" , this).text()
};
}).get() : res;
var input = document.getElementById(search_id);
var resultContent = document.getElementById(content_id);
var inputEventFunction = function() {
var searchText = input.value.trim().toLowerCase();
var keywords = searchText.split(/[\s\-]+/);
if (keywords.length > 1) {
keywords.push(searchText);
}
var resultItems = [];
if (searchText.length > 0) {
// perform local searching
datas.forEach(function(data) {
var isMatch = false;
var hitCount = 0;
var searchTextCount = 0;
var title = data.title.trim();
var titleInLowerCase = title.toLowerCase();
var content = data.content.trim().replace(/<[^>]+>/g,"");
var contentInLowerCase = content.toLowerCase();
var articleUrl = decodeURIComponent(data.url);
var indexOfTitle = [];
var indexOfContent = [];
// only match articles with not empty titles
if(title != '') {
keywords.forEach(function(keyword) {
function getIndexByWord(word, text, caseSensitive) {
var wordLen = word.length;
if (wordLen === 0) {
return [];
}
var startPosition = 0, position = [], index = [];
if (!caseSensitive) {
text = text.toLowerCase();
word = word.toLowerCase();
}
while ((position = text.indexOf(word, startPosition)) > -1) {
index.push({position: position, word: word});
startPosition = position + wordLen;
}
return index;
}
indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
});
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
isMatch = true;
hitCount = indexOfTitle.length + indexOfContent.length;
}
}
// show search results
if (isMatch) {
// sort index by position of keyword
[indexOfTitle, indexOfContent].forEach(function (index) {
index.sort(function (itemLeft, itemRight) {
if (itemRight.position !== itemLeft.position) {
return itemRight.position - itemLeft.position;
} else {
return itemLeft.word.length - itemRight.word.length;
}
});
});
// merge hits into slices
function mergeIntoSlice(text, start, end, index) {
var item = index[index.length - 1];
var position = item.position;
var word = item.word;
var hits = [];
var searchTextCountInSlice = 0;
while (position + word.length <= end && index.length != 0) {
if (word === searchText) {
searchTextCountInSlice++;
}
hits.push({position: position, length: word.length});
var wordEnd = position + word.length;
// move to next position of hit
index.pop();
while (index.length != 0) {
item = index[index.length - 1];
position = item.position;
word = item.word;
if (wordEnd > position) {
index.pop();
} else {
break;
}
}
}
searchTextCount += searchTextCountInSlice;
return {
hits: hits,
start: start,
end: end,
searchTextCount: searchTextCountInSlice
};
}
var slicesOfTitle = [];
if (indexOfTitle.length != 0) {
slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
}
var slicesOfContent = [];
while (indexOfContent.length != 0) {
var item = indexOfContent[indexOfContent.length - 1];
var position = item.position;
var word = item.word;
// cut out 100 characters
var start = position - 20;
var end = position + 80;
if(start < 0){
start = 0;
}
if (end < position + word.length) {
end = position + word.length;
}
if(end > content.length){
end = content.length;
}
slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
}
// sort slices in content by search text's count and hits' count
slicesOfContent.sort(function (sliceLeft, sliceRight) {
if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
return sliceRight.searchTextCount - sliceLeft.searchTextCount;
} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
return sliceRight.hits.length - sliceLeft.hits.length;
} else {
return sliceLeft.start - sliceRight.start;
}
});
// select top N slices in content
var upperBound = parseInt('1');
if (upperBound >= 0) {
slicesOfContent = slicesOfContent.slice(0, upperBound);
}
// highlight title and content
function highlightKeyword(text, slice) {
var result = '';
var prevEnd = slice.start;
slice.hits.forEach(function (hit) {
result += text.substring(prevEnd, hit.position);
var end = hit.position + hit.length;
result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
prevEnd = end;
});
result += text.substring(prevEnd, slice.end);
return result;
}
var resultItem = '';
if (slicesOfTitle.length != 0) {
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
} else {
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
}
slicesOfContent.forEach(function (slice) {
resultItem += "<a href='" + articleUrl + "'>" +
"<p class=\"search-result\">" + highlightKeyword(content, slice) +
"...</p>" + "</a>";
});
resultItem += "</li>";
resultItems.push({
item: resultItem,
searchTextCount: searchTextCount,
hitCount: hitCount,
id: resultItems.length
});
}
})
};
if (keywords.length === 1 && keywords[0] === "") {
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
} else if (resultItems.length === 0) {
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
} else {
resultItems.sort(function (resultLeft, resultRight) {
if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
return resultRight.searchTextCount - resultLeft.searchTextCount;
} else if (resultLeft.hitCount !== resultRight.hitCount) {
return resultRight.hitCount - resultLeft.hitCount;
} else {
return resultRight.id - resultLeft.id;
}
});
var searchResultList = '<ul class=\"search-result-list\">';
resultItems.forEach(function (result) {
searchResultList += result.item;
})
searchResultList += "</ul>";
resultContent.innerHTML = searchResultList;
}
}
if ('auto' === 'auto') {
input.addEventListener('input', inputEventFunction);
} else {
$('.search-icon').click(inputEventFunction);
input.addEventListener('keypress', function (event) {
if (event.keyCode === 13) {
inputEventFunction();
}
});
}
// remove loading animation
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
proceedsearch();
}
});
}
// handle and trigger popup window;
$('.popup-trigger').click(function(e) {
e.stopPropagation();
if (isfetched === false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};
});
$('.popup-btn-close').click(onPopupClose);
$('.popup').click(function(e){
e.stopPropagation();
});
$(document).on('keyup', function (event) {
var shouldDismissSearchPopup = event.which === 27 &&
$('.search-popup').is(':visible');
if (shouldDismissSearchPopup) {
onPopupClose();
}
});
</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script></body></html>