-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
1004 lines (793 loc) · 290 KB
/
atom.xml
File metadata and controls
1004 lines (793 loc) · 290 KB
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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>我在这里</title>
<subtitle>传说中的软件工程师</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://www.zhishuo.info/"/>
<updated>2017-01-03T01:57:19.000Z</updated>
<id>http://www.zhishuo.info/</id>
<author>
<name>klaus</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>JDK1.8源码之ConcurrentHashMap</title>
<link href="http://www.zhishuo.info/posts/java/JDK1.8%E6%BA%90%E7%A0%81%E4%B9%8BConcurrentHashMap.html"/>
<id>http://www.zhishuo.info/posts/java/JDK1.8源码之ConcurrentHashMap.html</id>
<published>2016-12-09T08:16:30.000Z</published>
<updated>2017-01-03T01:57:19.000Z</updated>
<content type="html"><![CDATA[<h3 id="什么是ConcurrentHashMap"><a href="#什么是ConcurrentHashMap" class="headerlink" title="什么是ConcurrentHashMap"></a>什么是ConcurrentHashMap</h3><p>以下观点都是建立在JDK1.8之上。</p>
<p>我们知道HashMap是非线程安全的,所以JDK给我们提供了几种线程安全的Map,<code>HashTable</code>,<code>Collections.SynchronizedMap</code>,<code>ConcurrentHashMap</code>几种线程安全的Map来使用。<br><code>HashTable</code>是每个方法都是synchronized修饰的。<br><code>Collections.SynchroinzedMap</code>则是用一个Object来当锁,每个方法里都使用<code>synchronized(obj)</code>来锁定。</p>
<p>这里没有理解以上的2种方法有什么区别,<code>HashTable</code>是作用在方法上的,所以锁的是this对象,后者是锁的Object,有什么区别吗?</p>
<p>最后一种<code>ConcurrentHashMap</code>则是使用了最新的锁技术来实现的。</p>
<h3 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h3><h3 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h3><h4 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 最大的阀值</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAXIMUM_CAPACITY = <span class="number">1</span> << <span class="number">30</span>;</div><div class="line"></div><div class="line"> <span class="comment">// 如果不指定长度,默认的长度</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_CAPACITY = <span class="number">16</span>;</div><div class="line"></div><div class="line"> <span class="comment">// 数组最大长度</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAX_ARRAY_SIZE = Integer.MAX_VALUE - <span class="number">8</span>;</div><div class="line"></div><div class="line"> <span class="comment">//</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_CONCURRENCY_LEVEL = <span class="number">16</span>;</div><div class="line"></div><div class="line"> <span class="comment">//与HashMap一样,负载因子也是0.75 当数组中有75%都有数据时就进行数组扩容</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> LOAD_FACTOR = <span class="number">0.75f</span>;</div><div class="line"></div><div class="line"> <span class="comment">// 当数组单个位置链表长度超过此值之后,会修改为树结构</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TREEIFY_THRESHOLD = <span class="number">8</span>;</div><div class="line"></div><div class="line"> <span class="comment">//当树结构节点小于6时,会修改为链表结构</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> UNTREEIFY_THRESHOLD = <span class="number">6</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The smallest table capacity for which bins may be treeified.</div><div class="line"> * (Otherwise the table is resized if too many nodes in a bin.)</div><div class="line"> * The value should be at least 4 * TREEIFY_THRESHOLD to avoid</div><div class="line"> * conflicts between resizing and treeification thresholds.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MIN_TREEIFY_CAPACITY = <span class="number">64</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Minimum number of rebinnings per transfer step. Ranges are</div><div class="line"> * subdivided to allow multiple resizer threads. This value</div><div class="line"> * serves as a lower bound to avoid resizers encountering</div><div class="line"> * excessive memory contention. The value should be at least</div><div class="line"> * DEFAULT_CAPACITY.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MIN_TRANSFER_STRIDE = <span class="number">16</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The number of bits used for generation stamp in sizeCtl.</div><div class="line"> * Must be at least 6 for 32bit arrays.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> RESIZE_STAMP_BITS = <span class="number">16</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The maximum number of threads that can help resize.</div><div class="line"> * Must fit in 32 - RESIZE_STAMP_BITS bits.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAX_RESIZERS = (<span class="number">1</span> << (<span class="number">32</span> - RESIZE_STAMP_BITS)) - <span class="number">1</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The bit shift for recording size stamp in sizeCtl.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> RESIZE_STAMP_SHIFT = <span class="number">32</span> - RESIZE_STAMP_BITS;</div><div class="line"></div><div class="line"> <span class="comment">/*</span></div><div class="line"> * Encodings for Node hash fields. See above for explanation.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MOVED = -<span class="number">1</span>; <span class="comment">// hash for forwarding nodes</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TREEBIN = -<span class="number">2</span>; <span class="comment">// hash for roots of trees</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> RESERVED = -<span class="number">3</span>; <span class="comment">// hash for transient reservations</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> HASH_BITS = <span class="number">0x7fffffff</span>; <span class="comment">// usable bits of normal node hash</span></div><div class="line"> </div><div class="line"> <span class="comment">// cpu数量</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> NCPU = Runtime.getRuntime().availableProcessors();</div><div class="line"> </div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The array of bins. Lazily initialized upon first insertion.</div><div class="line"> * Size is always a power of two. Accessed directly by iterators.</div><div class="line"> */</div><div class="line"> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node<K,V>[] table;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The next table to use; non-null only while resizing.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node<K,V>[] nextTable;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Base counter value, used mainly when there is no contention,</div><div class="line"> * but also as a fallback during table initialization</div><div class="line"> * races. Updated via CAS.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="keyword">long</span> baseCount;</div><div class="line"></div><div class="line"> <span class="comment">// 阀值用来控制数组是否要扩容,-1时表示正在初始化,-(1+n表示有几个线程在操作)</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="keyword">int</span> sizeCtl;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * The next table index (plus one) to split while resizing.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="keyword">int</span> transferIndex;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="keyword">int</span> cellsBusy;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Table of counter cells. When non-null, size is a power of 2.</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> CounterCell[] counterCells;</div></pre></td></tr></table></figure>
<h4 id="UnSafe相关"><a href="#UnSafe相关" class="headerlink" title="UnSafe相关"></a>UnSafe相关</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe U;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> SIZECTL;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> TRANSFERINDEX;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> BASECOUNT;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> CELLSBUSY;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> CELLVALUE;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> ABASE;</div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ASHIFT;</div><div class="line"></div><div class="line"><span class="keyword">static</span> {</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> U = sun.misc.Unsafe.getUnsafe();</div><div class="line"> Class<?> k = ConcurrentHashMap.class;</div><div class="line"> <span class="comment">//获取sizeCtl变量在内存中的偏移量</span></div><div class="line"> SIZECTL = U.objectFieldOffset</div><div class="line"> (k.getDeclaredField(<span class="string">"sizeCtl"</span>));</div><div class="line"> TRANSFERINDEX = U.objectFieldOffset</div><div class="line"> (k.getDeclaredField(<span class="string">"transferIndex"</span>));</div><div class="line"> BASECOUNT = U.objectFieldOffset</div><div class="line"> (k.getDeclaredField(<span class="string">"baseCount"</span>));</div><div class="line"> CELLSBUSY = U.objectFieldOffset</div><div class="line"> (k.getDeclaredField(<span class="string">"cellsBusy"</span>));</div><div class="line"> Class<?> ck = CounterCell.class;</div><div class="line"> CELLVALUE = U.objectFieldOffset</div><div class="line"> (ck.getDeclaredField(<span class="string">"value"</span>));</div><div class="line"> Class<?> ak = Node[].class;</div><div class="line"> ABASE = U.arrayBaseOffset(ak);</div><div class="line"> <span class="keyword">int</span> scale = U.arrayIndexScale(ak);</div><div class="line"> <span class="keyword">if</span> ((scale & (scale - <span class="number">1</span>)) != <span class="number">0</span>)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">"data type scale not a power of two"</span>);</div><div class="line"> ASHIFT = <span class="number">31</span> - Integer.numberOfLeadingZeros(scale);</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> Error(e);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">ConcurrentHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (initialCapacity < <span class="number">0</span>)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</div><div class="line"> <span class="comment">/** 这里直接判断了如果指定的值大于了最大长度的1/2,就直接等于最大值了</span></div><div class="line"> * 这里的 MAXIMUM_CAPACITY >>> 1 等于 MAXIMUM_CAPACITY / 2</div><div class="line"> * initialCapacity + (initialCapacity >>> 1) + 1</div><div class="line"> * 这里我没明白为什么要做这些操作,可能是为了求出在做散列时</div><div class="line"> * 更合适的值</div><div class="line"> */ </div><div class="line"> <span class="keyword">int</span> cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> <span class="number">1</span>)) ?</div><div class="line"> MAXIMUM_CAPACITY :</div><div class="line"> tableSizeFor(initialCapacity + (initialCapacity >>> <span class="number">1</span>) + <span class="number">1</span>));</div><div class="line"> <span class="keyword">this</span>.sizeCtl = cap;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>构造方法都很类似,这里我只举例一个。</p>
<h4 id="put方法"><a href="#put方法" class="headerlink" title="put方法"></a>put方法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div></pre></td><td class="code"><pre><div 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>{</div><div 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();</div><div class="line"> <span class="comment">//根据key的hashcode再次求hash</span></div><div class="line"> <span class="keyword">int</span> hash = spread(key.hashCode());</div><div class="line"> <span class="keyword">int</span> binCount = <span class="number">0</span>;</div><div class="line"> <span class="comment">//开始操作数组 这里是个死循环 会在插入数据成功后break掉</span></div><div class="line"> <span class="keyword">for</span> (Node<K,V>[] tab = table;;) {</div><div class="line"> Node<K,V> f; <span class="keyword">int</span> n, i, fh;</div><div class="line"> <span class="comment">//数组并没有在构造方法里初始化,所以在第一次put的时候,会初始化数组</span></div><div class="line"> <span class="keyword">if</span> (tab == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</div><div class="line"> <span class="comment">//这里我理解的是这样的,因为外层是个死循环,所以第一次执行到这的时候会进行初始化</span></div><div class="line"> <span class="comment">//然后就进入了下一次循环,因为已经初始化完毕了,所以就会进入别的分支判断</span></div><div class="line"> tab = initTable();</div><div 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>) {</div><div class="line"> <span class="comment">/** 进入这个判断的条件是在数组下标位置上没有节点,就证明是个新节点可以直接插入</span></div><div class="line"> * 所以就进入了这层,在这层插入的时候,因为只用了一个if判断是用的cas操作</div><div class="line"> * 所以有可能会失败,当失败时,还是因为外层是个死循环,所以会一直执行这个插入</div><div class="line"> * 直到插入成功,break掉</div><div class="line"> * 并且f 和i 也顺便在这里进行了赋值 i等于数组长度-1 与上 hash </div><div class="line"> * f 等于 数组 i 位置上的元素 </div><div class="line"> * </div><div class="line"> * /</div><div class="line"> if (casTabAt(tab, i, null,</div><div class="line"> new Node<K,V>(hash, key, value, null)))</div><div class="line"> break; // no lock when adding to empty bin</div><div class="line"> }</div><div class="line"> else if ((fh = f.hash) == MOVED)</div><div class="line"> tab = helpTransfer(tab, f);</div><div class="line"> else {</div><div class="line"> /**</div><div class="line"> * 到这个判断,就证明数组下标位置现在是有节点的,所以会有2种操作,链表和树的操作</div><div class="line"> * 首先在这里锁住了首节点</div><div class="line"> */</div><div class="line"> V oldVal = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">synchronized</span> (f) {</div><div class="line"> <span class="comment">//由与上面f 和 i 已经赋值过了,所以这里再次进行了确认是否是同一个对象</span></div><div class="line"> <span class="keyword">if</span> (tabAt(tab, i) == f) {</div><div class="line"> <span class="keyword">if</span> (fh >= <span class="number">0</span>) {</div><div class="line"> binCount = <span class="number">1</span>;</div><div class="line"> <span class="comment">// 以下这个循环是对链表进行循环</span></div><div class="line"> <span class="keyword">for</span> (Node<K,V> e = f;; ++binCount) {</div><div class="line"> K ek;</div><div class="line"> <span class="comment">// 如果hash值 和key 完全相等,就证明是同一个对象</span></div><div class="line"> <span class="comment">// 如果没有设置替换新值到这里就结束了</span></div><div class="line"> <span class="comment">// 如果设置了就替换新值,并且结束</span></div><div class="line"> <span class="keyword">if</span> (e.hash == hash &&</div><div class="line"> ((ek = e.key) == key ||</div><div class="line"> (ek != <span class="keyword">null</span> && key.equals(ek)))) {</div><div class="line"> oldVal = e.val;</div><div class="line"> <span class="keyword">if</span> (!onlyIfAbsent)</div><div class="line"> e.val = value;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">//这里是循环完整个链表都没有发现有相等的key</span></div><div class="line"> <span class="comment">//直接在链表最后插入新的节点,并且结束</span></div><div class="line"> Node<K,V> pred = e;</div><div class="line"> <span class="keyword">if</span> ((e = e.next) == <span class="keyword">null</span>) {</div><div class="line"> pred.next = <span class="keyword">new</span> Node<K,V>(hash, key,</div><div class="line"> value, <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) {</div><div class="line"> Node<K,V> p;</div><div class="line"> binCount = <span class="number">2</span>;</div><div class="line"> <span class="comment">//这里判断,如果节点是树类型的,则把节点放入到树结构中</span></div><div class="line"> <span class="comment">//同链表一样,如果设置了需要覆盖新值,更新一下</span></div><div class="line"> <span class="keyword">if</span> ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,</div><div class="line"> value)) != <span class="keyword">null</span>) {</div><div class="line"> oldVal = p.val;</div><div class="line"> <span class="keyword">if</span> (!onlyIfAbsent)</div><div class="line"> p.val = value;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="comment">//binCount 在树中是写死的2 在链表中会随着操作的增加而增加</span></div><div class="line"> <span class="keyword">if</span> (binCount != <span class="number">0</span>) {</div><div class="line"> <span class="comment">//如果超过了设置的值,就需要把链表转换成树结构</span></div><div class="line"> <span class="keyword">if</span> (binCount >= TREEIFY_THRESHOLD)</div><div class="line"> <span class="comment">//转树操作,但里面还会有扩容</span></div><div class="line"> treeifyBin(tab, i);</div><div class="line"> <span class="comment">// 因为上边的操作存储了oldVal,如果不为空直接返回此值,结束</span></div><div class="line"> <span class="keyword">if</span> (oldVal != <span class="keyword">null</span>)</div><div class="line"> <span class="keyword">return</span> oldVal;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> addCount(<span class="number">1L</span>, binCount);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>put方法总结:</p>
<blockquote>
<p>存放对象的时候,第一次操作,会先初始化底层的数组对象,然后再进行后续的操作<br>在存放元素的时候有这么几种情况:<br>1.如果计算出的hash值与上数组长度得出的下标位置为null,则可以直接插入<br>2.如果当前元素的hash值为MOVED,就证明有线程在进行扩容,就帮助一起扩容<br>3.都没有的情况下,证明当前下标存在其他元素,则进入元素追加,如果是链表就进行链表的操作,如果是树,就进行树的操作,最后添加元素完成。<br>4.添加完元素,如果需要进行树的转换,进行转换。<br>5.增加计数器,完成所有操作。 </p>
</blockquote>
<p>treeifyBin方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div 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>{</div><div class="line"> Node<K,V> b; <span class="keyword">int</span> n, sc;</div><div class="line"> <span class="keyword">if</span> (tab != <span class="keyword">null</span>) {</div><div class="line"> <span class="comment">//如果当前数组长度小于64,会直接扩容2倍,而不把当前节点轩换为树。</span></div><div class="line"> <span class="keyword">if</span> ((n = tab.length) < MIN_TREEIFY_CAPACITY)</div><div class="line"> tryPresize(n << <span class="number">1</span>);</div><div class="line"> <span class="comment">//获取index位置的节点,并且判断hash值为正常的节点,因为有可能存在-1等情况</span></div><div 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>) {</div><div class="line"> <span class="comment">//锁住首节点</span></div><div class="line"> <span class="keyword">synchronized</span> (b) {</div><div class="line"> <span class="keyword">if</span> (tabAt(tab, index) == b) {</div><div class="line"> TreeNode<K,V> hd = <span class="keyword">null</span>, tl = <span class="keyword">null</span>;</div><div class="line"> <span class="comment">//遍历所有节点,转换为树节点</span></div><div class="line"> <span class="keyword">for</span> (Node<K,V> e = b; e != <span class="keyword">null</span>; e = e.next) {</div><div class="line"> TreeNode<K,V> p =</div><div class="line"> <span class="keyword">new</span> TreeNode<K,V>(e.hash, e.key, e.val,</div><div class="line"> <span class="keyword">null</span>, <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> ((p.prev = tl) == <span class="keyword">null</span>)</div><div class="line"> hd = p;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> tl.next = p;</div><div class="line"> tl = p;</div><div class="line"> }</div><div class="line"> <span class="comment">//然后把TreeNode包装成TreeBin做为数组下标的对象</span></div><div class="line"> setTabAt(tab, index, <span class="keyword">new</span> TreeBin<K,V>(hd));</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>treeifyBin方法总结:</p>
<blockquote>
<p>如果当前数组长度小于64,则不进行树元素的转换,直接扩容成2倍,如果不是的话,那就对节点进行转换,从链表节点,转换为树节点。</p>
</blockquote>
<p>tryPresize方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div 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>{</div><div class="line"><span class="comment">//根据给出的长度,计算出实际的长度</span></div><div class="line"> <span class="keyword">int</span> c = (size >= (MAXIMUM_CAPACITY >>> <span class="number">1</span>)) ? MAXIMUM_CAPACITY :</div><div class="line"> tableSizeFor(size + (size >>> <span class="number">1</span>) + <span class="number">1</span>);</div><div class="line"> <span class="keyword">int</span> sc;</div><div class="line"> <span class="comment">//sc>=0的条件下才会进入SizeCtl的具体值见定义</span></div><div class="line"> <span class="keyword">while</span> ((sc = sizeCtl) >= <span class="number">0</span>) {</div><div class="line"> Node<K,V>[] tab = table; <span class="keyword">int</span> n;</div><div class="line"> <span class="comment">//数组没有初始化的情况,也就是一次都没有初始化,我个人感觉不会执行到这呢?</span></div><div class="line"> <span class="keyword">if</span> (tab == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>) {</div><div class="line"> n = (sc > c) ? sc : c;</div><div class="line"> <span class="comment">//把sizeCtl值修改为-1,为正在处理中</span></div><div class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc, -<span class="number">1</span>)) {</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">//这里再次判断了2个对象是否是一个对象,估计是为了防止其他对象操作?</span></div><div class="line"> <span class="keyword">if</span> (table == tab) {</div><div class="line"> <span class="comment">//初始化数组</span></div><div class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</div><div class="line"> Node<K,V>[] nt = (Node<K,V>[])<span class="keyword">new</span> Node<?,?>[n];</div><div class="line"> table = nt;</div><div class="line"> sc = n - (n >>> <span class="number">2</span>);</div><div class="line"> }</div><div class="line"> } <span class="keyword">finally</span> {</div><div class="line"> <span class="comment">//最后修改sizeCtl的值,这里为什么没有用CAS操作,我理解是因为前边的if里已经修改为了-1,其他的线程不能操作,所以这里只用了普通的赋值</span></div><div class="line"> sizeCtl = sc;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="comment">//如果没到0.75数,或者已经大于最大值,停止此方法</span></div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (c <= sc || n >= MAXIMUM_CAPACITY)</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="comment">//到这里就证明数组是已经初始化过了的,并且需要扩容</span></div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (tab == table) {</div><div class="line"> <span class="keyword">int</span> rs = resizeStamp(n);</div><div class="line"> <span class="comment">//这里是判断sc的值小于0证明在处理</span></div><div class="line"> <span class="keyword">if</span> (sc < <span class="number">0</span>) {</div><div class="line"> Node<K,V>[] nt;</div><div class="line"> <span class="comment">//没看懂什么意思</span></div><div class="line"> <span class="keyword">if</span> ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + <span class="number">1</span> ||</div><div class="line"> sc == rs + MAX_RESIZERS || (nt = nextTable) == <span class="keyword">null</span> ||</div><div class="line"> transferIndex <= <span class="number">0</span>)</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="comment">//把sizeCtl值+1并且进行扩容</span></div><div class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc, sc + <span class="number">1</span>))</div><div class="line"> transfer(tab, nt);</div><div class="line"> }</div><div class="line"> <span class="comment">//(rs << RESIZE_STAMP_SHIFT) + 2)没看懂求出来的这个值是什么,sc的值替换成这个之后,也进行扩容</span></div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc,</div><div class="line"> (rs << RESIZE_STAMP_SHIFT) + <span class="number">2</span>))</div><div class="line"> transfer(tab, <span class="keyword">null</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>tryPresize方法总结:</p>
<blockquote>
<p>这里就是对数组的一些异常情况做了兼容处理,最终保证扩容完成。</p>
</blockquote>
<p>transfer方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div></pre></td><td class="code"><pre><div 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>{</div><div class="line"> <span class="keyword">int</span> n = tab.length, stride;</div><div class="line"> <span class="comment">//求出处理数组的线程数,最大是16个线程</span></div><div 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)</div><div class="line"> stride = MIN_TRANSFER_STRIDE; <span class="comment">// subdivide range</span></div><div class="line"> <span class="keyword">if</span> (nextTab == <span class="keyword">null</span>) { <span class="comment">// initiating</span></div><div class="line"> <span class="comment">//构造nextTab,长度为原来的2倍</span></div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</div><div class="line"> Node<K,V>[] nt = (Node<K,V>[])<span class="keyword">new</span> Node<?,?>[n << <span class="number">1</span>];</div><div class="line"> nextTab = nt;</div><div class="line"> } <span class="keyword">catch</span> (Throwable ex) { <span class="comment">// try to cope with OOME</span></div><div class="line"> <span class="comment">//如果失败,则设置sc的最大值为int的最大值</span></div><div class="line"> sizeCtl = Integer.MAX_VALUE;</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> nextTable = nextTab;</div><div class="line"> <span class="comment">//这里我理解的就是,因为是原来的2倍,所以转换的下标是n</span></div><div class="line"> transferIndex = n;</div><div class="line"> }</div><div class="line"> <span class="keyword">int</span> nextn = nextTab.length;</div><div class="line"> <span class="comment">//构造节点元素,用来标明此节点是正在处理的节点,此节点的hash值为-1</span></div><div class="line"> ForwardingNode<K,V> fwd = <span class="keyword">new</span> ForwardingNode<K,V>(nextTab);</div><div class="line"> <span class="keyword">boolean</span> advance = <span class="keyword">true</span>;</div><div class="line"> <span class="keyword">boolean</span> finishing = <span class="keyword">false</span>; <span class="comment">// to ensure sweep before committing nextTab</span></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, bound = <span class="number">0</span>;;) {</div><div class="line"> Node<K,V> f; <span class="keyword">int</span> fh;</div><div class="line"> <span class="keyword">while</span> (advance) {</div><div class="line"> <span class="keyword">int</span> nextIndex, nextBound;</div><div class="line"> <span class="keyword">if</span> (--i >= bound || finishing)</div><div class="line"> advance = <span class="keyword">false</span>;</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((nextIndex = transferIndex) <= <span class="number">0</span>) {</div><div class="line"> i = -<span class="number">1</span>;</div><div class="line"> advance = <span class="keyword">false</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt</div><div class="line"> (<span class="keyword">this</span>, TRANSFERINDEX, nextIndex,</div><div class="line"> nextBound = (nextIndex > stride ?</div><div class="line"> nextIndex - stride : <span class="number">0</span>))) {</div><div class="line"> bound = nextBound;</div><div class="line"> i = nextIndex - <span class="number">1</span>;</div><div class="line"> advance = <span class="keyword">false</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (i < <span class="number">0</span> || i >= n || i + n >= nextn) {</div><div class="line"> <span class="keyword">int</span> sc;</div><div class="line"> <span class="keyword">if</span> (finishing) {</div><div class="line"> nextTable = <span class="keyword">null</span>;</div><div class="line"> table = nextTab;</div><div class="line"> sizeCtl = (n << <span class="number">1</span>) - (n >>> <span class="number">1</span>);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc = sizeCtl, sc - <span class="number">1</span>)) {</div><div class="line"> <span class="keyword">if</span> ((sc - <span class="number">2</span>) != resizeStamp(n) << RESIZE_STAMP_SHIFT)</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> finishing = advance = <span class="keyword">true</span>;</div><div class="line"> i = n; <span class="comment">// recheck before commit</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i)) == <span class="keyword">null</span>)</div><div class="line"> advance = casTabAt(tab, i, <span class="keyword">null</span>, fwd);</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</div><div class="line"> advance = <span class="keyword">true</span>; <span class="comment">// already processed</span></div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="comment">//锁住当前元素节点</span></div><div class="line"> <span class="keyword">synchronized</span> (f) {</div><div class="line"> <span class="comment">//再次确认节点是否相等,防止其他线程修改此节点</span></div><div class="line"> <span class="keyword">if</span> (tabAt(tab, i) == f) {</div><div class="line"> Node<K,V> ln, hn;</div><div class="line"> <span class="comment">//链表</span></div><div class="line"> <span class="keyword">if</span> (fh >= <span class="number">0</span>) {</div><div class="line"> <span class="comment">//这里我不太理解为什么是&n,因为在普通put值的时候是&(n-1),这里求出的其实也是个下标位置</span></div><div class="line"> <span class="keyword">int</span> runBit = fh & n;</div><div class="line"> Node<K,V> lastRun = f;</div><div class="line"> <span class="keyword">for</span> (Node<K,V> p = f.next; p != <span class="keyword">null</span>; p = p.next) {</div><div class="line"> <span class="comment">//这里对链表后面的元素也都进行了求值,但我个人理解这里的b应该绝对等于runBit才对啊,因为他们在put的时候被放入了同一个桶,就应该是hash值相同才对的</span></div><div class="line"> <span class="keyword">int</span> b = p.hash & n;</div><div class="line"> <span class="keyword">if</span> (b != runBit) {</div><div class="line"> runBit = b;</div><div class="line"> lastRun = p;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (runBit == <span class="number">0</span>) {</div><div class="line"> ln = lastRun;</div><div class="line"> hn = <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> hn = lastRun;</div><div class="line"> ln = <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">for</span> (Node<K,V> p = f; p != lastRun; p = p.next) {</div><div class="line"> <span class="keyword">int</span> ph = p.hash; K pk = p.key; V pv = p.val;</div><div class="line"> <span class="keyword">if</span> ((ph & n) == <span class="number">0</span>)</div><div class="line"> ln = <span class="keyword">new</span> Node<K,V>(ph, pk, pv, ln);</div><div class="line"> <span class="keyword">else</span></div><div class="line"> hn = <span class="keyword">new</span> Node<K,V>(ph, pk, pv, hn);</div><div class="line"> }</div><div class="line"> setTabAt(nextTab, i, ln);</div><div class="line"> setTabAt(nextTab, i + n, hn);</div><div class="line"> setTabAt(tab, i, fwd);</div><div class="line"> advance = <span class="keyword">true</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) {</div><div class="line"> TreeBin<K,V> t = (TreeBin<K,V>)f;</div><div class="line"> TreeNode<K,V> lo = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</div><div class="line"> TreeNode<K,V> hi = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">int</span> lc = <span class="number">0</span>, hc = <span class="number">0</span>;</div><div class="line"> <span class="keyword">for</span> (Node<K,V> e = t.first; e != <span class="keyword">null</span>; e = e.next) {</div><div class="line"> <span class="keyword">int</span> h = e.hash;</div><div class="line"> TreeNode<K,V> p = <span class="keyword">new</span> TreeNode<K,V></div><div class="line"> (h, e.key, e.val, <span class="keyword">null</span>, <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> ((h & n) == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> ((p.prev = loTail) == <span class="keyword">null</span>)</div><div class="line"> lo = p;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> loTail.next = p;</div><div class="line"> loTail = p;</div><div class="line"> ++lc;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">if</span> ((p.prev = hiTail) == <span class="keyword">null</span>)</div><div class="line"> hi = p;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> hiTail.next = p;</div><div class="line"> hiTail = p;</div><div class="line"> ++hc;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :</div><div class="line"> (hc != <span class="number">0</span>) ? <span class="keyword">new</span> TreeBin<K,V>(lo) : t;</div><div class="line"> hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :</div><div class="line"> (lc != <span class="number">0</span>) ? <span class="keyword">new</span> TreeBin<K,V>(hi) : t;</div><div class="line"> setTabAt(nextTab, i, ln);</div><div class="line"> setTabAt(nextTab, i + n, hn);</div><div class="line"> setTabAt(tab, i, fwd);</div><div class="line"> advance = <span class="keyword">true</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>transfer方法总结:<br>></p>
<p>addCount方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">addCount</span><span class="params">(<span class="keyword">long</span> x, <span class="keyword">int</span> check)</span> </span>{</div><div class="line"> CounterCell[] as; <span class="keyword">long</span> b, s;</div><div class="line"> <span class="comment">//使as等于counterCells 并且不等于null </span></div><div class="line"> <span class="comment">//或者在修改baseCount时失败,才会进入此方法</span></div><div class="line"> <span class="comment">//这里的x值,我个人理解的也就是此次增加了几个元素。</span></div><div class="line"> <span class="keyword">if</span> ((as = counterCells) != <span class="keyword">null</span> ||</div><div class="line"> !U.compareAndSwapLong(<span class="keyword">this</span>, BASECOUNT, b = baseCount, s = b + x)) {</div><div class="line"> CounterCell a; <span class="keyword">long</span> v; <span class="keyword">int</span> m;</div><div class="line"> <span class="keyword">boolean</span> uncontended = <span class="keyword">true</span>;</div><div class="line"> <span class="keyword">if</span> (as == <span class="keyword">null</span> || (m = as.length - <span class="number">1</span>) < <span class="number">0</span> ||</div><div class="line"> (a = as[ThreadLocalRandom.getProbe() & m]) == <span class="keyword">null</span> ||</div><div class="line"> !(uncontended =</div><div class="line"> U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {</div><div class="line"> fullAddCount(x, uncontended);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (check <= <span class="number">1</span>)</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> s = sumCount();</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (check >= <span class="number">0</span>) {</div><div class="line"> Node<K,V>[] tab, nt; <span class="keyword">int</span> n, sc;</div><div class="line"> <span class="keyword">while</span> (s >= (<span class="keyword">long</span>)(sc = sizeCtl) && (tab = table) != <span class="keyword">null</span> &&</div><div class="line"> (n = tab.length) < MAXIMUM_CAPACITY) {</div><div class="line"> <span class="keyword">int</span> rs = resizeStamp(n);</div><div class="line"> <span class="keyword">if</span> (sc < <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + <span class="number">1</span> ||</div><div class="line"> sc == rs + MAX_RESIZERS || (nt = nextTable) == <span class="keyword">null</span> ||</div><div class="line"> transferIndex <= <span class="number">0</span>)</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc, sc + <span class="number">1</span>))</div><div class="line"> transfer(tab, nt);</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc,</div><div class="line"> (rs << RESIZE_STAMP_SHIFT) + <span class="number">2</span>))</div><div class="line"> transfer(tab, <span class="keyword">null</span>);</div><div class="line"> s = sumCount();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<h4 id="remove方法"><a href="#remove方法" class="headerlink" title="remove方法"></a>remove方法</h4><h4 id="get方法"><a href="#get方法" class="headerlink" title="get方法"></a>get方法</h4><h4 id="其他相关"><a href="#其他相关" class="headerlink" title="其他相关"></a>其他相关</h4><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3>]]></content>
<summary type="html">
<h3 id="什么是ConcurrentHashMap"><a href="#什么是ConcurrentHashMap" class="headerlink" title="什么是ConcurrentHashMap"></a>什么是ConcurrentHashMap</h3><
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="jdk" scheme="http://www.zhishuo.info/tags/jdk/"/>
<category term="jdk源码" scheme="http://www.zhishuo.info/tags/jdk%E6%BA%90%E7%A0%81/"/>
<category term="ConcurrentHashMap" scheme="http://www.zhishuo.info/tags/ConcurrentHashMap/"/>
</entry>
<entry>
<title>JDK源码之LinkedHashMap</title>
<link href="http://www.zhishuo.info/posts/java/JDK%E6%BA%90%E7%A0%81%E4%B9%8BLinkedHashMap.html"/>
<id>http://www.zhishuo.info/posts/java/JDK源码之LinkedHashMap.html</id>
<published>2016-12-04T05:18:58.000Z</published>
<updated>2016-12-04T14:46:02.000Z</updated>
<content type="html"><![CDATA[<h2 id="LinkedHashMap使用场景"><a href="#LinkedHashMap使用场景" class="headerlink" title="LinkedHashMap使用场景"></a>LinkedHashMap使用场景</h2><p>我们都知道平常使用的HashMap是存放无顺序的,但当我们需要有顺序的HashMap的时候呢?所以JDK提供了<code>LinkedHashMap</code>和<code>TreeMap</code>2种有序的Map供我们使用。虽然以前一直都看过说<code>LinkedHashMap</code>是通过链表实现的,但一直没有去源码里探索究竟,今天看了之后原来和自己理解的不完全一样,理论的这些东西,还是自己真正去研究过才有底气。<br><code>LinkedHashMap</code>还支持插入顺序和访问顺序2种方式,默认的就是按照插入顺序排序的,如果需要按照访问顺序排序,在初始化时设置<code>accessOrder</code>为true即可。通过这个访问顺序我们也可以实现简单的LRU算法。</p>
<h2 id="源码概要"><a href="#源码概要" class="headerlink" title="源码概要"></a>源码概要</h2><p><code>LinkedHashMap</code>继承了<code>HashMap</code>,所以好多方法都是直接用的<code>HashMap</code>中的,在控制底层数据这方面,并没有选择自己去实现,而是通过重写了<code>HashMap</code>中的某些节点方法,来完成相应的功能。</p>
<p>比如通过重写一些对节点的操作来实现了自己的链表结构,增,删,改,查,几乎都是用的<code>HashMap</code>中的方法,但是在遍历的时候,以及在需要对象顺序的地方都重写了自己有序实现,这样即保持了功能性,又避免了开发重复的功能。</p>
<a id="more"></a>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><h3 id="类变量"><a href="#类变量" class="headerlink" title="类变量"></a>类变量</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">transient</span> LinkedHashMap.Entry<K,V> head;</div><div class="line"></div><div class="line"><span class="keyword">transient</span> LinkedHashMap.Entry<K,V> tail;</div><div class="line"></div><div class="line"><span class="keyword">final</span> <span class="keyword">boolean</span> accessOrder;</div></pre></td></tr></table></figure>
<p>这里我们可以看到,有两个链表节点,一个头一个尾,是用来维护链表的关系的。<code>accessOrder</code>是用来控制访问顺序的。至于这里变量为什么要用<code>transient</code>来修饰<strong>不知道为什么</strong>,虽然知道这个变量是用来控制在序列化时,该属性不会被写入文件。</p>
<h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity,</span></span></div><div class="line"> <span class="keyword">float</span> loadFactor,</div><div class="line"> <span class="keyword">boolean</span> accessOrder) {</div><div class="line"> <span class="keyword">super</span>(initialCapacity, loadFactor);</div><div class="line"> <span class="keyword">this</span>.accessOrder = accessOrder;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>除了最基本的构造方法之外,<code>LinkedHashMap</code>还提供了一个可以指定顺序的构造方法,我们可以看到这里构造方法也都是调用的<code>HashMap</code>中的构造方法,<code>accessOrder</code>就是可以指定是按插入排序还是按照访问排序。</p>
<h3 id="put方法"><a href="#put方法" class="headerlink" title="put方法"></a>put方法</h3><p><code>LinkedHashMap</code>并没有重写put方法,而是重写了put方法中的<code>newNode</code>方法,所以底层的数据存储和<code>HashMap</code>其实是一样的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function">Node<K,V> <span class="title">newNode</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, Node<K,V> e)</span> </span>{</div><div class="line"> LinkedHashMap.Entry<K,V> p =</div><div class="line"> <span class="keyword">new</span> LinkedHashMap.Entry<K,V>(hash, key, value, e);</div><div class="line"> linkNodeLast(p);</div><div class="line"> <span class="keyword">return</span> p;</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>在这里我们可以看到,除了正常的新建节点之外,还调用了<code>linkNodeLast(p)</code>这个方法,这里就是主要的具体实现了,就是通过这里来自己维护了一个链表。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">linkNodeLast</span><span class="params">(LinkedHashMap.Entry<K,V> p)</span> </span>{</div><div class="line"> LinkedHashMap.Entry<K,V> last = tail;</div><div class="line"> tail = p;</div><div class="line"> <span class="keyword">if</span> (last == <span class="keyword">null</span>)</div><div class="line"> head = p;</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> p.before = last;</div><div class="line"> last.after = p;</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>如果是首个节点,head和tail是指向同一个节点的。<br><img src="http://ogflhfadi.bkt.clouddn.com/%E9%93%BE%E8%A1%A8%E5%88%9D%E5%A7%8B%E5%8C%96.png" alt="链表初始化"></p>
<p>如果不是首节点,则进入正常的双向链表结构。<br><img src="http://ogflhfadi.bkt.clouddn.com/%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8%E6%AD%A3%E5%B8%B8%E7%BB%93%E6%9E%84.png" alt="正常结构"><br>这里其实就是前驱和后继节点都互相指向,前驱节点也有指向后继节点的连接,后继节点也有指向前驱节点的连接。不向单向链表一样,只是前节点有后节点的连接。<br>每次放入一个新的节点,都会把自己设置为尾节点。所以链表的添加速度还是很快的。</p>
<h4 id="关于HashMap"><a href="#关于HashMap" class="headerlink" title="关于HashMap"></a>关于HashMap</h4><p>如果大家看过我之前的关于HashMap源码的文章,会发现有几个我没弄懂的地方,没想到是在<code>LinkedHashMap</code>中使用的。<br><code>afterNodeAccess()</code>和<code>afterNodeInsertion()</code>这2个方法,在<code>HashMap</code>中都是空实现,原来是为了在<code>LinkedHashMap</code>中使用。</p>
<h5 id="afterNodeAccess"><a href="#afterNodeAccess" class="headerlink" title="afterNodeAccess"></a>afterNodeAccess</h5><p>这个方法在HashMap put方法中是如果数据已经存在才会调用的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeAccess</span><span class="params">(Node<K,V> e)</span> </span>{ <span class="comment">// move node to last</span></div><div class="line"> LinkedHashMap.Entry<K,V> last;</div><div class="line"> <span class="keyword">if</span> (accessOrder && (last = tail) != e) {</div><div class="line"> LinkedHashMap.Entry<K,V> p =</div><div class="line"> (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;</div><div class="line"> p.after = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span> (b == <span class="keyword">null</span>)</div><div class="line"> head = a;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> b.after = a;</div><div class="line"> <span class="keyword">if</span> (a != <span class="keyword">null</span>)</div><div class="line"> a.before = b;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> last = b;</div><div class="line"> <span class="keyword">if</span> (last == <span class="keyword">null</span>)</div><div class="line"> head = p;</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> p.before = last;</div><div class="line"> last.after = p;</div><div class="line"> }</div><div class="line"> tail = p;</div><div class="line"> ++modCount;</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>这里还是比较绕的,让我们来一一的分析。<br><code>if (accessOrder && (last = tail) != e)</code><br>这个判断的条件是设置的是访问顺序排序,并且当前操作的这个节点是不是最后一个节点。因为在HashMap中几乎所有对节点操作的方法都调用了此方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">LinkedHashMap.Entry<K,V> p =</div><div class="line"> (LinkedHashMap.Entry<K,V>)e,</div><div class="line"> b = p.before,</div><div class="line"> a = p.after;</div><div class="line"> p.after = <span class="keyword">null</span>;</div></pre></td></tr></table></figure></p>
<p>这个赋值看起来也比较恶心,这里拆开分析下。</p>
<ul>
<li>首先,当前操作的节点e赋值给了p,也就是当前操作节点。</li>
<li>p.before赋值给了b,也就是当前操作节点的前驱节点。</li>
<li>p.after赋值给了a,也就是当前操作节点的后继节点。</li>
<li>p.after给了null,证明把p设置为了尾节点。</li>
</ul>
<p>在后面的判断条件中又分了不同的情况,在不同的判断我会用不同的图画一下。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (b == <span class="keyword">null</span>)</div><div class="line"> head = a;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> b.after = a;</div></pre></td></tr></table></figure>
<p>这个判断的意思是</p>
<ul>
<li>如果<code>b==null</code>就证明该节点是头节点,所以直接把head设置为了该节点的后继节点。</li>
<li>如果<code>b!=null</code>,就证明该节点不是头节点,所以前驱节点的后继设置为了自己节点的后继也就是<code>a</code>。这个可以说起来比较绕,意思就是,把自己从中间移除了,把前驱和后继连接了起来。</li>
</ul>
<p>举例说明一下:</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/%E9%A6%96%E8%8A%82%E7%82%B9.png" alt="首节点"></p>
<p>上图这里假设我们操作的节点是head节点,所以b是等于null的,所以代码里直接把head设置为了a,在图中也就是data1变成了首节点。</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/%E9%9D%9E%E9%A6%96%E8%8A%82%E7%82%B9.png" alt="非首节点"><br>上图这里我们假设操作的节点是data1节点,所以b是不等于null的,所以把b.after也就是head的后继设置为data2。但p本身也就是data1的前驱还没有设置,我这里图没有体现出来。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (a != <span class="keyword">null</span>)</div><div class="line"> a.before = b;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> last = b;</div></pre></td></tr></table></figure>
<p>这个判断的意思是</p>
<ul>
<li>如果<code>a==null</code>,就证明该节点p是尾节点,所以把b赋值给了last,至于这里为什么这么弄,最下面判断会提及。<strong>这里我有个问题,因为我感觉这里是防御式编程,如果a==null的情况最外层的判断应该都进不来。如果大家知道请给我留言指教</strong></li>
<li>如果<code>a!=null</code>就证明不是尾节点,把当前节点p的后继节点也就是a的前驱设置为自己节点的前驱。<br>这个和上图第2个是相辅的,上图只是设置了前驱节点的连接,这里是设置了后继节点的连接。</li>
</ul>
<p>同样,如下图:</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/%E5%B0%BE%E8%8A%82%E7%82%B9.png" alt="尾节点"><br>上图如果当前操作的是尾节点,则a就等于null,所以设置了last=b。</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/%E9%9D%9E%E5%B0%BE%E8%8A%82%E7%82%B9.png" alt="非尾节点"><br>如上图,如果操作的非尾节点,这里继续假设我们操作的是data1,因为是非尾节点,所以把a.before设置为了b,设置完成后如图所示,head和data2连接上了,因为在上边的判断中head的后继已经设置过了,因此到这里基本前驱和后继的关系已经建立成功了,再往下就是把当前的操作节点p设置为尾节点。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (last == <span class="keyword">null</span>)</div><div class="line"> head = p;</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> p.before = last;</div><div class="line"> last.after = p;</div><div class="line"> }</div></pre></td></tr></table></figure>
<ul>
<li>如果<code>last==null</code>就证明还没有存放数据,所以直接设置了<code>head=p</code>,也就是首节点。</li>
<li>如果<code>last!=null</code>,就证明已经有数据存在,所以设置了当前操作节点p的前驱为last,last的后继设置为p,也就是把2个节点互相关联。last的取值有2个,一个是尾节点,也就是刚进入时候的大判断,一个是上边提到过的<code>a==null</code>的时候,所以last始终是最后一个节点。</li>
</ul>
<p><img src="http://ogflhfadi.bkt.clouddn.com/%E6%9C%80%E5%90%8E%E7%BB%93%E6%9E%84.png" alt="最后结构"><br>如果上图,这里继续假设我们操作的是data1,因为last取舍是tail,所以last不等于null,所以这里就把p和last互相连接最终形成了图下半部分的结构,其实就是data1换了个位置,换成最后了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">tail = p;</div><div class="line">++modCount;</div></pre></td></tr></table></figure>
<p>最后,把tail设置成最后操作的节点p,并且修改次数+1。</p>
<h5 id="afterNodeInsertion"><a href="#afterNodeInsertion" class="headerlink" title="afterNodeInsertion"></a>afterNodeInsertion</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeInsertion</span><span class="params">(<span class="keyword">boolean</span> evict)</span> </span>{ <span class="comment">// possibly remove eldest</span></div><div class="line"> LinkedHashMap.Entry<K,V> first;</div><div class="line"> <span class="keyword">if</span> (evict && (first = head) != <span class="keyword">null</span> && removeEldestEntry(first)) {</div><div class="line"> K key = first.key;</div><div class="line"> removeNode(hash(key), key, <span class="keyword">null</span>, <span class="keyword">false</span>, <span class="keyword">true</span>);</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>这里没太看明白,为什么要删除首节点?而且这里的<code>removeEldestEntry</code>方法是返回fasle的,估计是让自己去实现,重写这个类吧。</p>
<h4 id="newTreeNode"><a href="#newTreeNode" class="headerlink" title="newTreeNode"></a>newTreeNode</h4><p>同node,不做过多解释。</p>
<h3 id="remove方法"><a href="#remove方法" class="headerlink" title="remove方法"></a>remove方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeRemoval</span><span class="params">(Node<K,V> e)</span> </span>{ <span class="comment">// unlink</span></div><div class="line"> LinkedHashMap.Entry<K,V> p =</div><div class="line"> (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;</div><div class="line"> p.before = p.after = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span> (b == <span class="keyword">null</span>)</div><div class="line"> head = a;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> b.after = a;</div><div class="line"> <span class="keyword">if</span> (a == <span class="keyword">null</span>)</div><div class="line"> tail = b;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> a.before = b;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>同样,remove方法也是重写了<code>afterNodeRemoval</code>。这里其实就是一个前驱后继的关联这里就不多说了。</p>
<h3 id="get方法"><a href="#get方法" class="headerlink" title="get方法"></a>get方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>{</div><div class="line"> Node<K,V> e;</div><div class="line"> <span class="keyword">if</span> ((e = getNode(hash(key), key)) == <span class="keyword">null</span>)</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span> (accessOrder)</div><div class="line"> afterNodeAccess(e);</div><div class="line"> <span class="keyword">return</span> e.value;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>get方法这里是重写了的,这里同理,先是直接调用HashMap中的查找方法,如果需要设置访问顺序排序,就进行操作,否则,直接返回数据。</p>
<h3 id="迭代器"><a href="#迭代器" class="headerlink" title="迭代器"></a>迭代器</h3><p>增,删,改,查的操作,其实和HashMap很类似,并且也都是共用了一些父类的方法。因为自己给护了一套链表,所以在迭代器中,这些东西就是自己的实现了。</p>
<p>示例代码:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"></div><div class="line"> Map map = <span class="keyword">new</span> LinkedHashMap();</div><div class="line"> map.put(<span class="string">"0"</span>, <span class="string">"a"</span>);</div><div class="line"> map.put(<span class="string">"1"</span>, <span class="string">"b"</span>);</div><div class="line"> map.put(<span class="string">"2"</span>, <span class="string">"c"</span>);</div><div class="line"> Iterator it = map.entrySet().iterator();</div><div class="line"> <span class="keyword">while</span> (it.hasNext()){</div><div class="line"> System.out.println(it.next());</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>输出结果:<br><code>0=a
1=b
2=c</code></p>
<p><code>entrySet()</code>返回的是<code>LinkedEntrySet</code>这个类。此类是<code>LinkedHashMap</code>中的内部类。<br><code>LinkedEntrySet</code>中的<code>iterator</code>方法返回的是<code>LinkedEntryIterator</code>这个内部类。<br><code>LinkedEntryIterator</code>类最终继承了<code>LinkedHashIterator</code>这个类,所以最终的实现都在这里。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">LinkedHashIterator</span> </span>{</div><div class="line"> LinkedHashMap.Entry<K,V> next;</div><div class="line"> LinkedHashMap.Entry<K,V> current;</div><div class="line"> <span class="keyword">int</span> expectedModCount;</div><div class="line"></div><div class="line"> LinkedHashIterator() {</div><div class="line"> next = head;</div><div class="line"> expectedModCount = modCount;</div><div class="line"> current = <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> next != <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">final</span> LinkedHashMap.<span class="function">Entry<K,V> <span class="title">nextNode</span><span class="params">()</span> </span>{</div><div class="line"> LinkedHashMap.Entry<K,V> e = next;</div><div class="line"> <span class="keyword">if</span> (modCount != expectedModCount)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ConcurrentModificationException();</div><div class="line"> <span class="keyword">if</span> (e == <span class="keyword">null</span>)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</div><div class="line"> current = e;</div><div class="line"> next = e.after;</div><div class="line"> <span class="keyword">return</span> e;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>{</div><div class="line"> Node<K,V> p = current;</div><div class="line"> <span class="keyword">if</span> (p == <span class="keyword">null</span>)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException();</div><div class="line"> <span class="keyword">if</span> (modCount != expectedModCount)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ConcurrentModificationException();</div><div class="line"> current = <span class="keyword">null</span>;</div><div class="line"> K key = p.key;</div><div class="line"> removeNode(hash(key), key, <span class="keyword">null</span>, <span class="keyword">false</span>, <span class="keyword">false</span>);</div><div class="line"> expectedModCount = modCount;</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>这里可以看到经典的迭代器实现,我们最开始学的时候自己写迭代器就是这样的。<br>构造的时候把<code>next=head</code>所以判断hasNext很简单,只要判断next是否为空即可,因为在执行next方法的时候,next的引用指向也会变的。</p>
<p>nextNode()方法首先是判断是否安全删除的,这里是这样的如果在迭代器中,使用了Map自带的删除,迭代器就会抛出错误,所以,在迭代器中,只能使用迭代器的删除功能。然后设置current为当前节点,把next引用指向更新为下一个节点引用,返回节点。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>看完源码之后,更新了我对<code>LinkedHashMap</code>的认识,以前只是认为简单的双向链表实现的,没想到和<code>HashMap</code>有着莫大的关系。</p>
<p>个人认为<code>LinkedHashMap</code>虽然实现了有序,但是多维护了一层链表,所以我认为它的性能有可能会比<code>HashMap</code>是差的,但我用JDK1.8做了一下实验,插入100W条数据<code>LinkedHashMap</code>几乎是<code>HashMap</code>的一半。这里我自己就不是很能理解了,为什么多维护了数据,性能反而更快了呢?</p>
<p>这里我们也看到<code>LinkedHashMap</code>的优点和缺点几乎和<code>HashMap</code>是一样的,所以如果我们需要有序的<code>Map</code>,这是个很好的选择。</p>
]]></content>
<summary type="html">
<h2 id="LinkedHashMap使用场景"><a href="#LinkedHashMap使用场景" class="headerlink" title="LinkedHashMap使用场景"></a>LinkedHashMap使用场景</h2><p>我们都知道平常使用的HashMap是存放无顺序的,但当我们需要有顺序的HashMap的时候呢?所以JDK提供了<code>LinkedHashMap</code>和<code>TreeMap</code>2种有序的Map供我们使用。虽然以前一直都看过说<code>LinkedHashMap</code>是通过链表实现的,但一直没有去源码里探索究竟,今天看了之后原来和自己理解的不完全一样,理论的这些东西,还是自己真正去研究过才有底气。<br><code>LinkedHashMap</code>还支持插入顺序和访问顺序2种方式,默认的就是按照插入顺序排序的,如果需要按照访问顺序排序,在初始化时设置<code>accessOrder</code>为true即可。通过这个访问顺序我们也可以实现简单的LRU算法。</p>
<h2 id="源码概要"><a href="#源码概要" class="headerlink" title="源码概要"></a>源码概要</h2><p><code>LinkedHashMap</code>继承了<code>HashMap</code>,所以好多方法都是直接用的<code>HashMap</code>中的,在控制底层数据这方面,并没有选择自己去实现,而是通过重写了<code>HashMap</code>中的某些节点方法,来完成相应的功能。</p>
<p>比如通过重写一些对节点的操作来实现了自己的链表结构,增,删,改,查,几乎都是用的<code>HashMap</code>中的方法,但是在遍历的时候,以及在需要对象顺序的地方都重写了自己有序实现,这样即保持了功能性,又避免了开发重复的功能。</p>
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="jdk" scheme="http://www.zhishuo.info/tags/jdk/"/>
<category term="jdk源码" scheme="http://www.zhishuo.info/tags/jdk%E6%BA%90%E7%A0%81/"/>
<category term="LinkedHashMap" scheme="http://www.zhishuo.info/tags/LinkedHashMap/"/>
</entry>
<entry>
<title>JDK源码之ThreadLocal</title>
<link href="http://www.zhishuo.info/posts/java/JDK%E6%BA%90%E7%A0%81%E4%B9%8BThreadLocal.html"/>
<id>http://www.zhishuo.info/posts/java/JDK源码之ThreadLocal.html</id>
<published>2016-11-26T13:07:05.000Z</published>
<updated>2016-11-27T10:46:04.000Z</updated>
<content type="html"><![CDATA[<h4 id="ThreadLocal介绍"><a href="#ThreadLocal介绍" class="headerlink" title="ThreadLocal介绍"></a>ThreadLocal介绍</h4><p>一般我们都是如果发现有资源需要共享的时候,在多个线程之间要互相共享数据的时候,我们可以使用<code>ThreadLocal</code>来实现。因为存入<code>ThreadLocal</code>中的数据是和每个线程绑定的,所以不会存在数据竞争的问题了。</p>
<h4 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h4><p>例子如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocalTest</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> executeThread();</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">executeThread</span><span class="params">()</span> </span>{</div><div class="line"> ExecutorService executor = Executors.newFixedThreadPool(<span class="number">10</span>);</div><div class="line"> executor.submit(<span class="keyword">new</span> MyThread());</div><div class="line"> executor.submit(<span class="keyword">new</span> MyThread());</div><div class="line"> executor.submit(<span class="keyword">new</span> MyThread());</div><div class="line"> executor.shutdown();</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"> ThreadLocal threadLocal = <span class="keyword">new</span> ThreadLocal();</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> String str = Thread.currentThread() + <span class="string">"_"</span> + Math.random();</div><div class="line"> threadLocal.set(str);</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> Thread.sleep(<span class="number">2000</span>);</div><div class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> System.out.println(threadLocal.get());</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<a id="more"></a>
<h4 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h4><h5 id="set方法"><a href="#set方法" class="headerlink" title="set方法"></a>set方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(T value)</span> </span>{</div><div class="line"> Thread t = Thread.currentThread();</div><div class="line"> ThreadLocalMap map = getMap(t);</div><div class="line"> <span class="keyword">if</span> (map != <span class="keyword">null</span>)</div><div class="line"> map.set(<span class="keyword">this</span>, value);</div><div class="line"> <span class="keyword">else</span></div><div class="line"> createMap(t, value);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>可以看到这里面的逻辑很简单,得到当前线程,根据当前线程获取map,如果map存在,则直接替换,如果不存在则去初始化map并且放入其中。这里核心的代码是在初始化map时<code>createMap</code>方法和<code>map.set()</code>,我们进入方法其中看一下这两个方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {</div><div class="line"> table = <span class="keyword">new</span> Entry[INITIAL_CAPACITY];</div><div class="line"> <span class="keyword">int</span> i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - <span class="number">1</span>);</div><div class="line"> table[i] = <span class="keyword">new</span> Entry(firstKey, firstValue);</div><div class="line"> size = <span class="number">1</span>;</div><div class="line"> setThreshold(INITIAL_CAPACITY);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>可以看到<code>createMap</code>中最后调用了<code>ThreadLocalMap</code>类初始化,这个类看起来还是很简单的和HashMap类似,先建立一个数组,然后对key值取hash值并且对数组长度取与计算,求出在数组中的位置,然后把对象放入数组位置,最后设置阀值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(ThreadLocal<?> key, Object value)</span> </span>{</div><div class="line"></div><div class="line"> <span class="comment">// We don't use a fast path as with get() because it is at</span></div><div class="line"> <span class="comment">// least as common to use set() to create new entries as</span></div><div class="line"> <span class="comment">// it is to replace existing ones, in which case, a fast</span></div><div class="line"> <span class="comment">// path would fail more often than not.</span></div><div class="line"></div><div class="line"> Entry[] tab = table;</div><div class="line"> <span class="keyword">int</span> len = tab.length;</div><div class="line"> <span class="keyword">int</span> i = key.threadLocalHashCode & (len-<span class="number">1</span>);</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (Entry e = tab[i];</div><div class="line"> e != <span class="keyword">null</span>;</div><div class="line"> e = tab[i = nextIndex(i, len)]) {</div><div class="line"> ThreadLocal<?> k = e.get();</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (k == key) {</div><div class="line"> e.value = value;</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (k == <span class="keyword">null</span>) {</div><div class="line"> replaceStaleEntry(key, value, i);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> tab[i] = <span class="keyword">new</span> Entry(key, value);</div><div class="line"> <span class="keyword">int</span> sz = ++size;</div><div class="line"> <span class="keyword">if</span> (!cleanSomeSlots(i, sz) && sz >= threshold)</div><div class="line"> rehash();</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>set方法和<code>HashMap</code>的put方法类似,只不过是这个没有链表了。会先在原有数组中循环寻找当前key,如果找到,则赋新值,返回值。如果在循环过程中,遇到有null的,还会顺便清理掉。如果在数组中没有找到,就证明是新的。把在上面求到的<code>i</code>下标值,设置为新的对象。设置之后如果到达阀值会进行一系列的扩容操作,这里就不细说了,因为和HashMap类似。</p>
<h5 id="get方法"><a href="#get方法" class="headerlink" title="get方法"></a>get方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> T <span class="title">get</span><span class="params">()</span> </span>{</div><div class="line"> Thread t = Thread.currentThread();</div><div class="line"> ThreadLocalMap map = getMap(t);</div><div class="line"> <span class="keyword">if</span> (map != <span class="keyword">null</span>) {</div><div class="line"> ThreadLocalMap.Entry e = map.getEntry(<span class="keyword">this</span>);</div><div class="line"> <span class="keyword">if</span> (e != <span class="keyword">null</span>) {</div><div class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</div><div class="line"> T result = (T)e.value;</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> setInitialValue();</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>这里的核心方法是<code>map.getEntry(this)</code>和<code>setInitialValue()</code>,后者的方法和上面的set方法类似是为了初始化map就不细说了,这里我们主要说下<code>map.getEntry(this)</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> Entry <span class="title">getEntry</span><span class="params">(ThreadLocal<?> key)</span> </span>{</div><div class="line"> <span class="keyword">int</span> i = key.threadLocalHashCode & (table.length - <span class="number">1</span>);</div><div class="line"> Entry e = table[i];</div><div class="line"> <span class="keyword">if</span> (e != <span class="keyword">null</span> && e.get() == key)</div><div class="line"> <span class="keyword">return</span> e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> <span class="keyword">return</span> getEntryAfterMiss(key, i, e);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>可以看到这里主要就是在数组中寻找,如果能直接找到则直接返回,如果不能直接找到,则尝试去当前下标之后找,具体的查找方法请看<code>getEntryAfterMiss</code>个人理解的意思就是去不断的查找并且顺便清理为空的,如果找不到返回null。</p>
<h5 id="remove方法"><a href="#remove方法" class="headerlink" title="remove方法"></a>remove方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>{</div><div class="line"> ThreadLocalMap m = getMap(Thread.currentThread());</div><div class="line"> <span class="keyword">if</span> (m != <span class="keyword">null</span>)</div><div class="line"> m.remove(<span class="keyword">this</span>);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>其实到这里我们已经发现了,ThreadLocal主要的操作都是在对ThreadLocalMap的操作。这里同理,核心的代码是<code>m.remove(this)</code>,也就是map中的remove方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(ThreadLocal<?> key)</span> </span>{</div><div class="line"> Entry[] tab = table;</div><div class="line"> <span class="keyword">int</span> len = tab.length;</div><div class="line"> <span class="keyword">int</span> i = key.threadLocalHashCode & (len-<span class="number">1</span>);</div><div class="line"> <span class="keyword">for</span> (Entry e = tab[i];</div><div class="line"> e != <span class="keyword">null</span>;</div><div class="line"> e = tab[i = nextIndex(i, len)]) {</div><div class="line"> <span class="keyword">if</span> (e.get() == key) {</div><div class="line"> e.clear();</div><div class="line"> expungeStaleEntry(i);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>可以看到这里的方法其实也很简单,就是根据hashcode算出的下标开始在数组中查找,如果找到删除的对象,直接设置为null,这里我个人理解是这样的因为它放到数组的<code>Entry</code>对象是继承<code>WeakReference</code>的,所以当设置为null之后,垃圾回收器在回收时会优先回收。这里是个人理解的,如有不正确请各位指正。</p>
<h4 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h4><p>我们看下来<code>ThreadLocal</code>的代码,其实就是对数组的操作进行了封装,所以说如果有大量的写的话会产生和<code>ArrayList</code>类似的问题,就是不断的扩容。所以在使用时应该注意,不要有太大量的写入,并且在这里也没有看到像<code>ArrayList</code>那样可以指定长度的地方。以上就是个人对此类的理解,如果有不正确之处请大家指正共同进步。</p>
]]></content>
<summary type="html">
<h4 id="ThreadLocal介绍"><a href="#ThreadLocal介绍" class="headerlink" title="ThreadLocal介绍"></a>ThreadLocal介绍</h4><p>一般我们都是如果发现有资源需要共享的时候,在多个线程之间要互相共享数据的时候,我们可以使用<code>ThreadLocal</code>来实现。因为存入<code>ThreadLocal</code>中的数据是和每个线程绑定的,所以不会存在数据竞争的问题了。</p>
<h4 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h4><p>例子如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocalTest</span> </span>&#123;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</div><div class="line"> executeThread();</div><div class="line"></div><div class="line"> &#125;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">executeThread</span><span class="params">()</span> </span>&#123;</div><div class="line"> ExecutorService executor = Executors.newFixedThreadPool(<span class="number">10</span>);</div><div class="line"> executor.submit(<span class="keyword">new</span> MyThread());</div><div class="line"> executor.submit(<span class="keyword">new</span> MyThread());</div><div class="line"> executor.submit(<span class="keyword">new</span> MyThread());</div><div class="line"> executor.shutdown();</div><div class="line"></div><div class="line"> &#125;</div><div class="line"></div><div class="line"> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</div><div class="line"> ThreadLocal threadLocal = <span class="keyword">new</span> ThreadLocal();</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</div><div class="line"> String str = Thread.currentThread() + <span class="string">"_"</span> + Math.random();</div><div class="line"> threadLocal.set(str);</div><div class="line"> <span class="keyword">try</span> &#123;</div><div class="line"> Thread.sleep(<span class="number">2000</span>);</div><div class="line"> &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</div><div class="line"> e.printStackTrace();</div><div class="line"> &#125;</div><div class="line"> System.out.println(threadLocal.get());</div><div class="line"> &#125;</div><div class="line"> &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="jdk" scheme="http://www.zhishuo.info/tags/jdk/"/>
<category term="jdk源码" scheme="http://www.zhishuo.info/tags/jdk%E6%BA%90%E7%A0%81/"/>
</entry>
<entry>
<title>hexo博客主动推送到百度站长平台</title>
<link href="http://www.zhishuo.info/posts/hexo/hexo%E5%8D%9A%E5%AE%A2%E4%B8%BB%E5%8A%A8%E6%8E%A8%E9%80%81%E5%88%B0%E7%99%BE%E5%BA%A6%E7%AB%99%E9%95%BF%E5%B9%B3%E5%8F%B0.html"/>
<id>http://www.zhishuo.info/posts/hexo/hexo博客主动推送到百度站长平台.html</id>
<published>2016-11-26T09:19:09.000Z</published>
<updated>2016-11-26T09:39:15.000Z</updated>
<content type="html"><![CDATA[<h4 id="推送百度站长平台"><a href="#推送百度站长平台" class="headerlink" title="推送百度站长平台"></a>推送百度站长平台</h4><p>百度站长平台有几种方式提交自己的链接</p>
<ol>
<li>主动推送(实时)</li>
<li>自动推送</li>
<li>sitemap</li>
</ol>
<p>或者自己手工提交,这里主要说以上三种方式。</p>
<a id="more"></a>
<h5 id="主动推送"><a href="#主动推送" class="headerlink" title="主动推送"></a>主动推送</h5><p>第一种方式使用了<code>baidu_url_submitter</code>这个工具:<br>首先在hexo根目录安装插件<code>npm install hexo-baidu-url-submit --save</code><br>然后在根目录<code>_config.yml</code>中增加<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="attr">baidu_url_submit:</span></div><div class="line"><span class="attr"> count:</span> <span class="number">1</span> <span class="comment">## 提交的链接数</span></div><div class="line"><span class="attr"> host:</span> www.zhishuo.info <span class="comment">## 在百度站长平台中注册的域名</span></div><div class="line"><span class="attr"> token:</span> token <span class="comment">## 百度站长平台里的token,在链接提交-自动提交-主动推送中有</span></div><div class="line"><span class="attr"> path:</span> baidu_urls.txt <span class="comment">## 这个是会自动生成要推送的url地址</span></div></pre></td></tr></table></figure></p>
<p><code>count</code>是控制几条数据会生成在<code>baidu_urls.txt</code>中,这里推荐首次使用时填写你所有博客的数量,然后修改为1即可。<br>这里还要注意一个问题,站点配置文件中<code>url: http://www.zhishuo.info</code>这里一定要带上www要不然推送不成功。<br>最后把deploy调整一下,就可以正常使用了。<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="attr">deploy:</span></div><div class="line"><span class="attr"> - type:</span> git</div><div class="line"><span class="attr"> repository:</span> https://github.com/imkratos/imkratos.github.io.git</div><div class="line"><span class="attr"> branch:</span> master</div><div class="line"><span class="attr"> - type:</span> baidu_url_submitter <span class="comment"># baidu push</span></div></pre></td></tr></table></figure></p>
<ul>
<li><code>hexo g</code>会生成baidu_urls.txt文件。</li>
<li><code>hexo d</code>会在部署完git代码之后,把新增的url也会推送到百度。</li>
</ul>
<h5 id="自动推送"><a href="#自动推送" class="headerlink" title="自动推送"></a>自动推送</h5><p> 由于我是使用的hexo next主题,next主题中自带了此功能,在next主题根目录下<code>_config.yml</code>中找到<code>baidu_push: false</code>改为<code>true</code>即可。</p>
<h5 id="sitemap"><a href="#sitemap" class="headerlink" title="sitemap"></a>sitemap</h5><p> 此种方式好像被github官方禁止了,博主添加了发现效果不是很明显,需要使用的同学们可自行查找。</p>
<p> <code>baidu_url_submitter</code><a href="http://hui-wang.info/2016/10/23/Hexo%E6%8F%92%E4%BB%B6%E4%B9%8B%E7%99%BE%E5%BA%A6%E4%B8%BB%E5%8A%A8%E6%8F%90%E4%BA%A4%E9%93%BE%E6%8E%A5/" target="_blank" rel="external">作者</a></p>
]]></content>
<summary type="html">
<h4 id="推送百度站长平台"><a href="#推送百度站长平台" class="headerlink" title="推送百度站长平台"></a>推送百度站长平台</h4><p>百度站长平台有几种方式提交自己的链接</p>
<ol>
<li>主动推送(实时)</li>
<li>自动推送</li>
<li>sitemap</li>
</ol>
<p>或者自己手工提交,这里主要说以上三种方式。</p>
</summary>
<category term="hexo" scheme="http://www.zhishuo.info/categories/hexo/"/>
<category term="hexo" scheme="http://www.zhishuo.info/tags/hexo/"/>
<category term="百度站长" scheme="http://www.zhishuo.info/tags/%E7%99%BE%E5%BA%A6%E7%AB%99%E9%95%BF/"/>
</entry>
<entry>
<title>hexo博客next主题修改静态资源为CDN</title>
<link href="http://www.zhishuo.info/posts/hexo/hexo%E5%8D%9A%E5%AE%A2next%E4%B8%BB%E9%A2%98%E4%BF%AE%E6%94%B9%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E4%B8%BACDN.html"/>
<id>http://www.zhishuo.info/posts/hexo/hexo博客next主题修改静态资源为CDN.html</id>
<published>2016-11-15T15:55:21.000Z</published>
<updated>2016-11-17T14:40:20.000Z</updated>
<content type="html"><![CDATA[<h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>博主最近看到博客打开速度非常慢,点开chrome的开发者工具查看是由于css,js,图片加载太慢,故<code>css,js</code>换成了国内的cdn,图片换成了<a href="http://www.qiniu.com/" target="_blank" rel="external">七牛云</a>。<br>打开主题配置文件<code>_config.yml</code>以下为修改方式:<br><a id="more"></a><br><figure class="highlight yml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="attr">vendors:</span></div><div class="line"> <span class="comment"># Internal path prefix. Please do not edit it.</span></div><div class="line"><span class="attr"> _internal:</span> vendors</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 2.1.3</span></div><div class="line"><span class="attr"> jquery:</span> //cdn.bootcss.com/jquery/<span class="number">2.1</span><span class="number">.3</span>/jquery.min.js</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 2.1.5</span></div><div class="line"> <span class="comment"># Fancybox: http://fancyapps.com/fancybox/</span></div><div class="line"><span class="attr"> fancybox:</span> //cdn.bootcss.com/fancybox/<span class="number">2.1</span><span class="number">.5</span>/jquery.fancybox.pack.js</div><div class="line"><span class="attr"> fancybox_css:</span> //cdn.bootcss.com/fancybox/<span class="number">2.1</span><span class="number">.5</span>/jquery.fancybox.min.css</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 1.0.6</span></div><div class="line"><span class="attr"> fastclick:</span> //cdn.bootcss.com/fastclick/<span class="number">1.0</span><span class="number">.6</span>/fastclick.min.js</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 1.9.7</span></div><div class="line"><span class="attr"> lazyload:</span> //cdn.bootcss.com/jquery_lazyload/<span class="number">1.9</span><span class="number">.7</span>/jquery.lazyload.min.js</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 1.2.1</span></div><div class="line"><span class="attr"> velocity:</span> //cdn.bootcss.com/velocity/<span class="number">1.3</span><span class="number">.1</span>/velocity.min.js</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 1.2.1</span></div><div class="line"><span class="attr"> velocity_ui:</span> //cdn.bootcss.com/velocity/<span class="number">1.3</span><span class="number">.1</span>/velocity.ui.min.js</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 0.7.9</span></div><div class="line"><span class="attr"> ua_parser:</span> //cdn.bootcss.com/UAParser.js/<span class="number">0.7</span><span class="number">.12</span>/ua-parser.min.js</div><div class="line"></div><div class="line"> <span class="comment"># Internal version: 4.4.0</span></div><div class="line"> <span class="comment"># http://fontawesome.io/</span></div><div class="line"><span class="attr"> fontawesome:</span> //cdn.bootcss.com/font-awesome/<span class="number">4.6</span><span class="number">.2</span>/css/font-awesome.min.css</div></pre></td></tr></table></figure></p>
<p>最终打开时间由原来的20-40s,缩短到现在的5s左右,可能不稳定,但足够了。</p>
<p>以下是几个国内比较好的cdn网站</p>
<ul>
<li><a href="http://www.bootcdn.cn/" target="_blank" rel="external">http://www.bootcdn.cn/</a></li>
<li><a href="https://www.staticfile.org/" target="_blank" rel="external">https://www.staticfile.org/</a></li>
<li><a href="http://cdn.code.baidu.com/" target="_blank" rel="external">http://cdn.code.baidu.com/</a></li>
</ul>
]]></content>
<summary type="html">
<h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>博主最近看到博客打开速度非常慢,点开chrome的开发者工具查看是由于css,js,图片加载太慢,故<code>css,js</code>换成了国内的cdn,图片换成了<a href="http://www.qiniu.com/">七牛云</a>。<br>打开主题配置文件<code>_config.yml</code>以下为修改方式:<br>
</summary>
<category term="hexo" scheme="http://www.zhishuo.info/categories/hexo/"/>
<category term="hexo" scheme="http://www.zhishuo.info/tags/hexo/"/>
<category term="next" scheme="http://www.zhishuo.info/tags/next/"/>
<category term="cdn" scheme="http://www.zhishuo.info/tags/cdn/"/>
</entry>
<entry>
<title>JDK源码之HashMap</title>
<link href="http://www.zhishuo.info/posts/java/JDK%E6%BA%90%E7%A0%81%E4%B9%8BHashMap.html"/>
<id>http://www.zhishuo.info/posts/java/JDK源码之HashMap.html</id>
<published>2016-11-12T07:39:10.000Z</published>
<updated>2016-11-26T10:00:41.000Z</updated>
<content type="html"><![CDATA[<h4 id="什么是HashMap"><a href="#什么是HashMap" class="headerlink" title="什么是HashMap"></a>什么是HashMap</h4><p>HashMap是一个可以提供O(1)时间复杂度的数据结构,由数组和链表数据结构组成。在对存入的key进行hash之后,然后用hash值在数组上确定一个位置,把value对象以Node节点形式放入到数组的链表当中。</p>
<p>jdk1.8之后对此做了优化,因为如果发生了数据倾斜,可能会使数组某个下标的Node链表非常长,因为链表查询起来比较慢,所以1.8之后修改了,当Node链表长度大于8时,会把该下标位置的链表数据结构修改为红黑树的结构来保证查询的速度。当数据长度小于8时,会再修改为链表。</p>
<h4 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h4><p>个人理解使用场景应该是在不需要复杂的查询,只需要一个Key对应一个Value,写入少的场景。因为像HashMap,ArrayList这种数据结构都提供了自动扩容的功能,像HashMap的负载因子是0.75,也就是当数组中75%的位置都有值以后会进行扩容。每次扩容的时候都涉及到每个数据的rehash和数组的复制,所以当写入数据量非常大的时候,会不断的进行rehash和复制,有可能会造成CPU占用率非常高(这只是个人平时学习的理解,如果有不对之处请大家指正)。</p>
<p>所以个人感觉HashMap的使用场景也是读多写少的场景,可以提供很快速度的读,写入的速度也可以,但如果提前知道Map里要放入多少数据,最好在new对象的时候,就手动指定出长度,这样可以避免rehash,从来变相提高使用效率。</p>
<h4 id="源码实现-jdk1-8版本"><a href="#源码实现-jdk1-8版本" class="headerlink" title="源码实现 jdk1.8版本"></a>源码实现 jdk1.8版本</h4><p><strong>以下的代码都是代码在上面,解释在下面。</strong></p>
<h5 id="变量声明"><a href="#变量声明" class="headerlink" title="变量声明"></a>变量声明</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The default initial capacity - MUST be a power of two.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="number">1</span> << <span class="number">4</span>; <span class="comment">// aka 16</span></div></pre></td></tr></table></figure>
<p>这个值是HashMap默认初始化的长度,也就是16长。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The maximum capacity, used if a higher value is implicitly specified</div><div class="line"> * by either of the constructors with arguments.</div><div class="line"> * MUST be a power of two <= 1<<30.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAXIMUM_CAPACITY = <span class="number">1</span> << <span class="number">30</span>; <span class="comment">//1073741824</span></div></pre></td></tr></table></figure>
<p>这里按注释看,应该是HashMap支持的最大长度,如果超过这个值,则使用这个值。</p>
<a id="more"></a>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The load factor used when none specified in constructor.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> DEFAULT_LOAD_FACTOR = <span class="number">0.75f</span>;</div></pre></td></tr></table></figure>
<p>负载因子,当数组中有值的位数超过此阀值时,进行扩容。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The bin count threshold for using a tree rather than list for a</div><div class="line"> * bin. Bins are converted to trees when adding an element to a</div><div class="line"> * bin with at least this many nodes. The value must be greater</div><div class="line"> * than 2 and should be at least 8 to mesh with assumptions in</div><div class="line"> * tree removal about conversion back to plain bins upon</div><div class="line"> * shrinkage.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TREEIFY_THRESHOLD = <span class="number">8</span>;</div></pre></td></tr></table></figure>
<p>当数组单个位置超过此值后,会把数据结构修改为红黑树。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The bin count threshold for untreeifying a (split) bin during a</div><div class="line"> * resize operation. Should be less than TREEIFY_THRESHOLD, and at</div><div class="line"> * most 6 to mesh with shrinkage detection under removal.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> UNTREEIFY_THRESHOLD = <span class="number">6</span>;</div></pre></td></tr></table></figure>
<p>当小于这个值时,修改为链表。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The smallest table capacity for which bins may be treeified.</div><div class="line"> * (Otherwise the table is resized if too many nodes in a bin.)</div><div class="line"> * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts</div><div class="line"> * between resizing and treeification thresholds.</div><div class="line"> */</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MIN_TREEIFY_CAPACITY = <span class="number">64</span>;</div></pre></td></tr></table></figure>
<p>???</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">transient</span> Node<K,V>[] table;</div></pre></td></tr></table></figure>
<p>存放元素的数组。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * The next size value at which to resize (capacity * load factor).</div><div class="line"> *</div><div class="line"> * <span class="doctag">@serial</span></div><div class="line"> */</div><div class="line"> <span class="comment">// (The javadoc description is true upon serialization.</span></div><div class="line"> <span class="comment">// Additionally, if the table array has not been allocated, this</span></div><div class="line"> <span class="comment">// field holds the initial array capacity, or zero signifying</span></div><div class="line"> <span class="comment">// DEFAULT_INITIAL_CAPACITY.)</span></div><div class="line"> <span class="keyword">int</span> threshold;</div></pre></td></tr></table></figure>
<p>这个变量是用来存放阀值的,也就是数组长度*0.75的值。</p>
<h5 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * Constructs an empty <tt>HashMap</tt> with the default initial capacity</div><div class="line"> * (16) and the default load factor (0.75).</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">HashMap</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">this</span>.loadFactor = DEFAULT_LOAD_FACTOR; <span class="comment">// all other fields defaulted</span></div><div class="line"> }</div><div class="line"><span class="comment">/**</span></div><div class="line"> * Constructs an empty <tt>HashMap</tt> with the specified initial</div><div class="line"> * capacity and the default load factor (0.75).</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> initialCapacity the initial capacity.</div><div class="line"> * <span class="doctag">@throws</span> IllegalArgumentException if the initial capacity is negative.</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">HashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>{</div><div class="line"> <span class="keyword">this</span>(initialCapacity, DEFAULT_LOAD_FACTOR);</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Constructs an empty <tt>HashMap</tt> with the specified initial</div><div class="line"> * capacity and load factor.</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> initialCapacity the initial capacity</div><div class="line"> * <span class="doctag">@param</span> loadFactor the load factor</div><div class="line"> * <span class="doctag">@throws</span> IllegalArgumentException if the initial capacity is negative</div><div class="line"> * or the load factor is nonpositive</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">HashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity, <span class="keyword">float</span> loadFactor)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (initialCapacity < <span class="number">0</span>)</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal initial capacity: "</span> +</div><div class="line"> initialCapacity);</div><div class="line"> <span class="keyword">if</span> (initialCapacity > MAXIMUM_CAPACITY)</div><div class="line"> initialCapacity = MAXIMUM_CAPACITY;</div><div class="line"> <span class="keyword">if</span> (loadFactor <= <span class="number">0</span> || Float.isNaN(loadFactor))</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal load factor: "</span> +</div><div class="line"> loadFactor);</div><div class="line"> <span class="keyword">this</span>.loadFactor = loadFactor;</div><div class="line"> <span class="keyword">this</span>.threshold = tableSizeFor(initialCapacity);</div><div class="line"> }</div></pre></td></tr></table></figure>
<ul>
<li>初始化时,如果是空的构造方法,会只设置负载因子的值为默认的0.75.</li>
<li>如果指定的<code>initialCapacity</code>超过<code>MAXIMUM_CAPACITY</code>,则值就为<code>MAXIMUM_CAPACITY</code></li>
<li>tableSizeFor函数会求出一个值作为HashMap的容量</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Returns a power of two size for the given target capacity.</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">tableSizeFor</span><span class="params">(<span class="keyword">int</span> cap)</span> </span>{</div><div class="line"> <span class="keyword">int</span> n = cap - <span class="number">1</span>;</div><div class="line"> n |= n >>> <span class="number">1</span>;</div><div class="line"> n |= n >>> <span class="number">2</span>;</div><div class="line"> n |= n >>> <span class="number">4</span>;</div><div class="line"> n |= n >>> <span class="number">8</span>;</div><div class="line"> n |= n >>> <span class="number">16</span>;</div><div class="line"> <span class="keyword">return</span> (n < <span class="number">0</span>) ? <span class="number">1</span> : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + <span class="number">1</span>;</div><div class="line"> }</div></pre></td></tr></table></figure>
<ul>
<li>初始化完毕</li>
</ul>
<h5 id="put方法"><a href="#put方法" class="headerlink" title="put方法"></a>put方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div></pre></td><td class="code"><pre><div 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>{</div><div class="line"> <span class="keyword">return</span> putVal(hash(key), key, value, <span class="keyword">false</span>, <span class="keyword">true</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">boolean</span> onlyIfAbsent,</span></span></div><div class="line"> <span class="keyword">boolean</span> evict) {</div><div class="line"> Node<K,V>[] tab; Node<K,V> p; <span class="keyword">int</span> n, i;</div><div class="line"> <span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</div><div class="line"> n = (tab = resize()).length;</div><div class="line"> <span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) & hash]) == <span class="keyword">null</span>)</div><div class="line"> tab[i] = newNode(hash, key, value, <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> Node<K,V> e; K k;</div><div class="line"> <span class="keyword">if</span> (p.hash == hash &&</div><div class="line"> ((k = p.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> e = p;</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> e = ((TreeNode<K,V>)p).putTreeVal(<span class="keyword">this</span>, tab, hash, key, value);</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> binCount = <span class="number">0</span>; ; ++binCount) {</div><div class="line"> <span class="keyword">if</span> ((e = p.next) == <span class="keyword">null</span>) {</div><div class="line"> p.next = newNode(hash, key, value, <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> (binCount >= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></div><div class="line"> treeifyBin(tab, hash);</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (e.hash == hash &&</div><div class="line"> ((k = e.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> p = e;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (e != <span class="keyword">null</span>) { <span class="comment">// existing mapping for key</span></div><div class="line"> V oldValue = e.value;</div><div class="line"> <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="keyword">null</span>)</div><div class="line"> e.value = value;</div><div class="line"> afterNodeAccess(e);</div><div class="line"> <span class="keyword">return</span> oldValue;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> ++modCount;</div><div class="line"> <span class="keyword">if</span> (++size > threshold)</div><div class="line"> resize();</div><div class="line"> afterNodeInsertion(evict);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>说下参数的意思,<code>hash</code>就是通过key计算出的hash值,<code>Key Value</code>不用多说就是我们要放入的Key和Value,<code>onlyIfAbsent</code>这里我理解应该是,如果Key存在是否要替换对应Key的Value,这里传入的是<code>false</code>,<code>evict</code>应该是,是否自动扩容,默认是<code>true</code>。</p>
<p><strong>声明变量</strong><br><code>tab</code>数组,就是需要存放节点的数组。<br><code>p</code>则是放在数组下标位置的节点。<br><code>n,i</code>数组长度和位置下标。</p>
<p><strong>代码逻辑</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</div><div class="line"> n = (tab = resize()).length;</div></pre></td></tr></table></figure></p>
<p>如果数组没有初始化或者长度为0,则进行初始化。<br><code>resize()</code>方法中的初始化代码</p>
<h5 id="扩容机制"><a href="#扩容机制" class="headerlink" title="扩容机制"></a>扩容机制</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Initializes or doubles table size. If null, allocates in</div><div class="line"> * accord with initial capacity target held in field threshold.</div><div class="line"> * Otherwise, because we are using power-of-two expansion, the</div><div class="line"> * elements from each bin must either stay at same index, or move</div><div class="line"> * with a power of two offset in the new table.</div><div class="line"> *</div><div class="line"> * <span class="doctag">@return</span> the table</div><div class="line"> */</div><div class="line"> <span class="keyword">final</span> Node<K,V>[] resize() {</div><div class="line"> Node<K,V>[] oldTab = table;</div><div class="line"> <span class="keyword">int</span> oldCap = (oldTab == <span class="keyword">null</span>) ? <span class="number">0</span> : oldTab.length;</div><div class="line"> <span class="keyword">int</span> oldThr = threshold;</div><div class="line"> <span class="keyword">int</span> newCap, newThr = <span class="number">0</span>;</div><div class="line"> <span class="keyword">if</span> (oldCap > <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> (oldCap >= MAXIMUM_CAPACITY) {</div><div class="line"> threshold = Integer.MAX_VALUE;</div><div class="line"> <span class="keyword">return</span> oldTab;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((newCap = oldCap << <span class="number">1</span>) < MAXIMUM_CAPACITY &&</div><div class="line"> oldCap >= DEFAULT_INITIAL_CAPACITY)</div><div class="line"> newThr = oldThr << <span class="number">1</span>; <span class="comment">// double threshold</span></div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (oldThr > <span class="number">0</span>) <span class="comment">// initial capacity was placed in threshold</span></div><div class="line"> newCap = oldThr;</div><div class="line"> <span class="keyword">else</span> { <span class="comment">// zero initial threshold signifies using defaults</span></div><div class="line"> newCap = DEFAULT_INITIAL_CAPACITY;</div><div class="line"> newThr = (<span class="keyword">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (newThr == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">float</span> ft = (<span class="keyword">float</span>)newCap * loadFactor;</div><div class="line"> newThr = (newCap < MAXIMUM_CAPACITY && ft < (<span class="keyword">float</span>)MAXIMUM_CAPACITY ?</div><div class="line"> (<span class="keyword">int</span>)ft : Integer.MAX_VALUE);</div><div class="line"> }</div><div class="line"> threshold = newThr;</div><div class="line"> <span class="meta">@SuppressWarnings</span>({<span class="string">"rawtypes"</span>,<span class="string">"unchecked"</span>})</div><div class="line"> Node<K,V>[] newTab = (Node<K,V>[])<span class="keyword">new</span> Node[newCap];</div><div class="line"> table = newTab;</div><div class="line"> <span class="keyword">if</span> (oldTab != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < oldCap; ++j) {</div><div class="line"> Node<K,V> e;</div><div class="line"> <span class="keyword">if</span> ((e = oldTab[j]) != <span class="keyword">null</span>) {</div><div class="line"> oldTab[j] = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span> (e.next == <span class="keyword">null</span>)</div><div class="line"> newTab[e.hash & (newCap - <span class="number">1</span>)] = e;</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> ((TreeNode<K,V>)e).split(<span class="keyword">this</span>, newTab, j, oldCap);</div><div class="line"> <span class="keyword">else</span> { <span class="comment">// preserve order</span></div><div class="line"> Node<K,V> loHead = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</div><div class="line"> Node<K,V> hiHead = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</div><div class="line"> Node<K,V> next;</div><div class="line"> <span class="keyword">do</span> {</div><div class="line"> next = e.next;</div><div class="line"> <span class="keyword">if</span> ((e.hash & oldCap) == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> (loTail == <span class="keyword">null</span>)</div><div class="line"> loHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> loTail.next = e;</div><div class="line"> loTail = e;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">if</span> (hiTail == <span class="keyword">null</span>)</div><div class="line"> hiHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> hiTail.next = e;</div><div class="line"> hiTail = e;</div><div class="line"> }</div><div class="line"> } <span class="keyword">while</span> ((e = next) != <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> (loTail != <span class="keyword">null</span>) {</div><div class="line"> loTail.next = <span class="keyword">null</span>;</div><div class="line"> newTab[j] = loHead;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (hiTail != <span class="keyword">null</span>) {</div><div class="line"> hiTail.next = <span class="keyword">null</span>;</div><div class="line"> newTab[j + oldCap] = hiHead;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> newTab;</div><div class="line"> }</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (oldCap > <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> (oldCap >= MAXIMUM_CAPACITY) {</div><div class="line"> threshold = Integer.MAX_VALUE;</div><div class="line"> <span class="keyword">return</span> oldTab;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((newCap = oldCap << <span class="number">1</span>) < MAXIMUM_CAPACITY &&</div><div class="line"> oldCap >= DEFAULT_INITIAL_CAPACITY)</div><div class="line"> newThr = oldThr << <span class="number">1</span>; <span class="comment">// double threshold</span></div><div class="line"> }</div></pre></td></tr></table></figure>
<ol>
<li><p>这里判断现有的数组长度是否为0,如果当前数组长度大于0,并且大于或等于<code>MAXIMUM_CAPACITY</code>,则<code>threshold</code>等于int的最大值,返回当前数组。</p>
</li>
<li><p><code>newCap</code>的值等于当前数组的长度*2<code>old<<1</code>,如果这个新值小于<code>MAXIMUM_CAPACITY</code>并且当前数组的长度 >= <code>DEFAULT_INITIAL_CAPACITY</code>也就是16,<code>newThr = oldThr << 1;</code>这句话的意思是新的阀值=老阀值*2。其实以上的意思就是如果符合条件了,把新数组的长度和阀值都扩大2倍。</p>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">else</span> <span class="keyword">if</span> (oldThr > <span class="number">0</span>) <span class="comment">// initial capacity was placed in threshold</span></div><div class="line"> newCap = oldThr;</div></pre></td></tr></table></figure>
<p>这里的意思是,如果老数组长度不大于0,并且老阀值大于0,把老阀值的值赋给新的数组长度,<strong>这里我没理解为什么要这么做</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">else</span> { <span class="comment">// zero initial threshold signifies using defaults</span></div><div class="line"> newCap = DEFAULT_INITIAL_CAPACITY;</div><div class="line"> newThr = (<span class="keyword">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>如果以上条件都不满足,就会进行默认的初始化。也就是数组长度等于16,阀值等于12。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (newThr == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">float</span> ft = (<span class="keyword">float</span>)newCap * loadFactor;</div><div class="line"> newThr = (newCap < MAXIMUM_CAPACITY && ft < (<span class="keyword">float</span>)MAXIMUM_CAPACITY ?</div><div class="line"> (<span class="keyword">int</span>)ft : Integer.MAX_VALUE);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>至此如果新的阀值还是0的话,会再次进行初始化,公式还是一样的,先根据新的数组长度*负载因子计算出阀值,如果新的数据长度小于长度最大值,并且三目表达式用来判断是否过界,如果不过界,返回计算的值,如果过界,返回int最大值。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">threshold = newThr;</div><div class="line">Node<K,V>[] newTab = (Node<K,V>[])<span class="keyword">new</span> Node[newCap];</div><div class="line">table = newTab;</div></pre></td></tr></table></figure></p>
<p>到这里,基本上阀值和新的数组长度都已经初始化完了,创建了新的数组,开始初始化对象了。如果是构造方法里调用这里,到这里就已经结束了,因为数组,阀值,都已经初始化完毕了。<br>再下面的代码是扩容的时候会进行调用的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (oldTab != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < oldCap; ++j) {</div><div class="line"> Node<K,V> e;</div><div class="line"> <span class="keyword">if</span> ((e = oldTab[j]) != <span class="keyword">null</span>) {</div><div class="line"> oldTab[j] = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span> (e.next == <span class="keyword">null</span>)</div><div class="line"> newTab[e.hash & (newCap - <span class="number">1</span>)] = e;</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> ((TreeNode<K,V>)e).split(<span class="keyword">this</span>, newTab, j, oldCap);</div><div class="line"> <span class="keyword">else</span> { <span class="comment">// preserve order</span></div><div class="line"> Node<K,V> loHead = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</div><div class="line"> Node<K,V> hiHead = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</div><div class="line"> Node<K,V> next;</div><div class="line"> <span class="keyword">do</span> {</div><div class="line"> next = e.next;</div><div class="line"> <span class="keyword">if</span> ((e.hash & oldCap) == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> (loTail == <span class="keyword">null</span>)</div><div class="line"> loHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> loTail.next = e;</div><div class="line"> loTail = e;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">if</span> (hiTail == <span class="keyword">null</span>)</div><div class="line"> hiHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> hiTail.next = e;</div><div class="line"> hiTail = e;</div><div class="line"> }</div><div class="line"> } <span class="keyword">while</span> ((e = next) != <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> (loTail != <span class="keyword">null</span>) {</div><div class="line"> loTail.next = <span class="keyword">null</span>;</div><div class="line"> newTab[j] = loHead;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (hiTail != <span class="keyword">null</span>) {</div><div class="line"> hiTail.next = <span class="keyword">null</span>;</div><div class="line"> newTab[j + oldCap] = hiHead;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<ul>
<li>到第7行为止,是表示如果老数组的下标位置只有一个节点没有链表也没有红黑树,就会把该位置的数组赋值给新数组,在新数组的位置是hash值&数组的长度(这里我以前一直认为是取余)。</li>
<li>如果是红黑树,则进行树拆分<code>((TreeNode<K,V>)e).split(this, newTab, j, oldCap);</code></li>
</ul>
<h5 id="红黑树"><a href="#红黑树" class="headerlink" title="红黑树"></a>红黑树</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">split</span><span class="params">(HashMap<K,V> map, Node<K,V>[] tab, <span class="keyword">int</span> index, <span class="keyword">int</span> bit)</span> </span>{</div><div class="line"> TreeNode<K,V> b = <span class="keyword">this</span>;</div><div class="line"> <span class="comment">// Relink into lo and hi lists, preserving order</span></div><div class="line"> TreeNode<K,V> loHead = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</div><div class="line"> TreeNode<K,V> hiHead = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">int</span> lc = <span class="number">0</span>, hc = <span class="number">0</span>;</div><div class="line"> <span class="keyword">for</span> (TreeNode<K,V> e = b, next; e != <span class="keyword">null</span>; e = next) {</div><div class="line"> next = (TreeNode<K,V>)e.next;</div><div class="line"> e.next = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span> ((e.hash & bit) == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> ((e.prev = loTail) == <span class="keyword">null</span>)</div><div class="line"> loHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> loTail.next = e;</div><div class="line"> loTail = e;</div><div class="line"> ++lc;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">if</span> ((e.prev = hiTail) == <span class="keyword">null</span>)</div><div class="line"> hiHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> hiTail.next = e;</div><div class="line"> hiTail = e;</div><div class="line"> ++hc;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (loHead != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (lc <= UNTREEIFY_THRESHOLD)</div><div class="line"> tab[index] = loHead.untreeify(map);</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> tab[index] = loHead;</div><div class="line"> <span class="keyword">if</span> (hiHead != <span class="keyword">null</span>) <span class="comment">// (else is already treeified)</span></div><div class="line"> loHead.treeify(tab);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (hiHead != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (hc <= UNTREEIFY_THRESHOLD)</div><div class="line"> tab[index + bit] = hiHead.untreeify(map);</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> tab[index + bit] = hiHead;</div><div class="line"> <span class="keyword">if</span> (loHead != <span class="keyword">null</span>)</div><div class="line"> hiHead.treeify(tab);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>这里我大概理解是这样的,先对所有的节点进行遍历,然后通过一定条件来判断是否小于等于<code>UNTREEIFY_THRESHOLD</code>这个阀值,如果小于就切换为普通的链表,如果大于就继续在树上添加节点,由于博主对树现在理解不是很好,这里暂且这样,以后我会指定研究一下树的节构。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">else</span> { <span class="comment">// preserve order</span></div><div class="line"> Node<K,V> loHead = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</div><div class="line"> Node<K,V> hiHead = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</div><div class="line"> Node<K,V> next;</div><div class="line"> <span class="keyword">do</span> {</div><div class="line"> next = e.next;</div><div class="line"> <span class="keyword">if</span> ((e.hash & oldCap) == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">if</span> (loTail == <span class="keyword">null</span>)</div><div class="line"> loHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> loTail.next = e;</div><div class="line"> loTail = e;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">if</span> (hiTail == <span class="keyword">null</span>)</div><div class="line"> hiHead = e;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> hiTail.next = e;</div><div class="line"> hiTail = e;</div><div class="line"> }</div><div class="line"> } <span class="keyword">while</span> ((e = next) != <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> (loTail != <span class="keyword">null</span>) {</div><div class="line"> loTail.next = <span class="keyword">null</span>;</div><div class="line"> newTab[j] = loHead;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (hiTail != <span class="keyword">null</span>) {</div><div class="line"> hiTail.next = <span class="keyword">null</span>;</div><div class="line"> newTab[j + oldCap] = hiHead;</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>这里就是链表节构了,也是把链表循环先读取出来。但是这里为什么分成了2个链表?并且分别放在了不同的位置上。至此,resize()方法执行完毕,让我们再回来继续看put方法。</p>
<ul>
<li>以上我们写了resize()中详细的方法</li>
<li>有初始化数组的长度和阀值</li>
<li>对数组进行扩容时对链表,单节点,以及树不同的处理</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) & hash]) == <span class="keyword">null</span>)</div><div class="line"> tab[i] = newNode(hash, key, value, <span class="keyword">null</span>);</div></pre></td></tr></table></figure>
<p>这里是数组长度对hash值求与值,如果该位置为null证明该位置没有放入元素,则放入新的元素。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">Node<K,V> e; K k;</div><div class="line"> <span class="keyword">if</span> (p.hash == hash &&</div><div class="line"> ((k = p.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> e = p;</div></pre></td></tr></table></figure>
<p>这里判断,如果hash值相等并且key值相等,或者key的equest相等,就直接替换该位置的值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> e = ((TreeNode<K,V>)p).putTreeVal(<span class="keyword">this</span>, tab, hash, key, value);</div></pre></td></tr></table></figure>
<p>如果该位置的结构是树的,则调用树的插入方法,关于树的方法博主在这里暂不讨论,因为博主也不是特别理解树结构,以后会补充。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> binCount = <span class="number">0</span>; ; ++binCount) {</div><div class="line"> <span class="keyword">if</span> ((e = p.next) == <span class="keyword">null</span>) {</div><div class="line"> p.next = newNode(hash, key, value, <span class="keyword">null</span>);</div><div class="line"> <span class="keyword">if</span> (binCount >= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></div><div class="line"> treeifyBin(tab, hash);</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (e.hash == hash &&</div><div class="line"> ((k = e.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> p = e;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>如果是链表的话,循环所有的链表,如果有相等的key,直接替换该key对应的值,如果循环到最后也没有相等的,在链表上插入一个新的node节点,同时判断是否满足到达<code>TREEIFY_THRESHOLD</code>的条件,如果到达,会把数据结构变更为红黑树。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (e != <span class="keyword">null</span>) { <span class="comment">// existing mapping for key</span></div><div class="line"> V oldValue = e.value;</div><div class="line"> <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="keyword">null</span>)</div><div class="line"> e.value = value;</div><div class="line"> afterNodeAccess(e);</div><div class="line"> <span class="keyword">return</span> oldValue;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>如果到这里e已经存在了,因为e等于新放入的node节点。就把该节点移到链表最后一个,并且直接返回value值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">++modCount;</div><div class="line"> <span class="keyword">if</span> (++size > threshold)</div><div class="line"> resize();</div><div class="line"> afterNodeInsertion(evict);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div></pre></td></tr></table></figure>
<p>记录修改的次数<code>modCount</code>是用来控制非法修改hashmap里的值,来抛出<code>ConcurrentModificationException</code>。并且判断数组长度是否大于阀值,如果大于了就要调用<code>resize()</code>进行扩容,<code>afterNodeInsertion</code>没有理解作用是什么,至此put方法全部执行完毕。</p>
<h5 id="get方法"><a href="#get方法" class="headerlink" title="get方法"></a>get方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">final</span> Node<K,V> <span class="title">getNode</span><span class="params">(<span class="keyword">int</span> hash, Object key)</span> </span>{</div><div class="line"> Node<K,V>[] tab; Node<K,V> first, e; <span class="keyword">int</span> n; K k;</div><div class="line"> <span class="keyword">if</span> ((tab = table) != <span class="keyword">null</span> && (n = tab.length) > <span class="number">0</span> &&</div><div class="line"> (first = tab[(n - <span class="number">1</span>) & hash]) != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (first.hash == hash && <span class="comment">// always check first node</span></div><div class="line"> ((k = first.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> <span class="keyword">return</span> first;</div><div class="line"> <span class="keyword">if</span> ((e = first.next) != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (first <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> <span class="keyword">return</span> ((TreeNode<K,V>)first).getTreeNode(hash, key);</div><div class="line"> <span class="keyword">do</span> {</div><div class="line"> <span class="keyword">if</span> (e.hash == hash &&</div><div class="line"> ((k = e.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> <span class="keyword">return</span> e;</div><div class="line"> } <span class="keyword">while</span> ((e = e.next) != <span class="keyword">null</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>put方法核心的代码是这里,这里是判断如果在数据里找到当前key的位置之后,首先判断hash值,和key是否都相等,如果都相等,直接返回此对象,如果不相等还分2种情况一种是树结构,会调用树的查找方法,另外一种是链表,则会对链表不断的循环判断,直到找到相等元素为止。<br>如果最后都没有找到,则返回null。</p>
<h5 id="remove方法"><a href="#remove方法" class="headerlink" title="remove方法"></a>remove方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">final</span> Node<K,V> <span class="title">removeNode</span><span class="params">(<span class="keyword">int</span> hash, Object key, Object value,</span></span></div><div class="line"> <span class="keyword">boolean</span> matchValue, <span class="keyword">boolean</span> movable) {</div><div class="line"> Node<K,V>[] tab; Node<K,V> p; <span class="keyword">int</span> n, index;</div><div class="line"> <span class="keyword">if</span> ((tab = table) != <span class="keyword">null</span> && (n = tab.length) > <span class="number">0</span> &&</div><div class="line"> (p = tab[index = (n - <span class="number">1</span>) & hash]) != <span class="keyword">null</span>) {</div><div class="line"> Node<K,V> node = <span class="keyword">null</span>, e; K k; V v;</div><div class="line"> <span class="keyword">if</span> (p.hash == hash &&</div><div class="line"> ((k = p.key) == key || (key != <span class="keyword">null</span> && key.equals(k))))</div><div class="line"> node = p;</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((e = p.next) != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> node = ((TreeNode<K,V>)p).getTreeNode(hash, key);</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">do</span> {</div><div class="line"> <span class="keyword">if</span> (e.hash == hash &&</div><div class="line"> ((k = e.key) == key ||</div><div class="line"> (key != <span class="keyword">null</span> && key.equals(k)))) {</div><div class="line"> node = e;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> p = e;</div><div class="line"> } <span class="keyword">while</span> ((e = e.next) != <span class="keyword">null</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (node != <span class="keyword">null</span> && (!matchValue || (v = node.value) == value ||</div><div class="line"> (value != <span class="keyword">null</span> && value.equals(v)))) {</div><div class="line"> <span class="keyword">if</span> (node <span class="keyword">instanceof</span> TreeNode)</div><div class="line"> ((TreeNode<K,V>)node).removeTreeNode(<span class="keyword">this</span>, tab, movable);</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (node == p)</div><div class="line"> tab[index] = node.next;</div><div class="line"> <span class="keyword">else</span></div><div class="line"> p.next = node.next;</div><div class="line"> ++modCount;</div><div class="line"> --size;</div><div class="line"> afterNodeRemoval(node);</div><div class="line"> <span class="keyword">return</span> node;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>remove方法和get方法类似,核心代码是首先也是在数组里找位置,如果当前位置首个节点是这个key,就把值给node,如果当前值不是,则进行后续查找。如果是树,调用树的查找方法,把查找到的值给node,如果是链表,则对链表进行循环查找,找到之后把值给node。<br>最后判断如果node不这代,并且value值相等。则会进行删除动作,如果是树节构就调用树的删除方法,如果数组的位置首节点是要删除的值,则直接把next的值给当前位置,如果是链表后续的值,则把要删除的key的下一个节点,和上一个节点连接,自己就会被删除掉。<br>最后增加修改次数,size减掉。返回删除的节点。</p>
]]></content>
<summary type="html">
<h4 id="什么是HashMap"><a href="#什么是HashMap" class="headerlink" title="什么是HashMap"></a>什么是HashMap</h4><p>HashMap是一个可以提供O(1)时间复杂度的数据结构,由数组和链表数据结构组成。在对存入的key进行hash之后,然后用hash值在数组上确定一个位置,把value对象以Node节点形式放入到数组的链表当中。</p>
<p>jdk1.8之后对此做了优化,因为如果发生了数据倾斜,可能会使数组某个下标的Node链表非常长,因为链表查询起来比较慢,所以1.8之后修改了,当Node链表长度大于8时,会把该下标位置的链表数据结构修改为红黑树的结构来保证查询的速度。当数据长度小于8时,会再修改为链表。</p>
<h4 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h4><p>个人理解使用场景应该是在不需要复杂的查询,只需要一个Key对应一个Value,写入少的场景。因为像HashMap,ArrayList这种数据结构都提供了自动扩容的功能,像HashMap的负载因子是0.75,也就是当数组中75%的位置都有值以后会进行扩容。每次扩容的时候都涉及到每个数据的rehash和数组的复制,所以当写入数据量非常大的时候,会不断的进行rehash和复制,有可能会造成CPU占用率非常高(这只是个人平时学习的理解,如果有不对之处请大家指正)。</p>
<p>所以个人感觉HashMap的使用场景也是读多写少的场景,可以提供很快速度的读,写入的速度也可以,但如果提前知道Map里要放入多少数据,最好在new对象的时候,就手动指定出长度,这样可以避免rehash,从来变相提高使用效率。</p>
<h4 id="源码实现-jdk1-8版本"><a href="#源码实现-jdk1-8版本" class="headerlink" title="源码实现 jdk1.8版本"></a>源码实现 jdk1.8版本</h4><p><strong>以下的代码都是代码在上面,解释在下面。</strong></p>
<h5 id="变量声明"><a href="#变量声明" class="headerlink" title="变量声明"></a>变量声明</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</div><div class="line"> * The default initial capacity - MUST be a power of two.</div><div class="line"> */</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="number">1</span> &lt;&lt; <span class="number">4</span>; <span class="comment">// aka 16</span></div></pre></td></tr></table></figure>
<p>这个值是HashMap默认初始化的长度,也就是16长。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</div><div class="line"> * The maximum capacity, used if a higher value is implicitly specified</div><div class="line"> * by either of the constructors with arguments.</div><div class="line"> * MUST be a power of two &lt;= 1&lt;&lt;30.</div><div class="line"> */</span></div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAXIMUM_CAPACITY = <span class="number">1</span> &lt;&lt; <span class="number">30</span>; <span class="comment">//1073741824</span></div></pre></td></tr></table></figure>
<p>这里按注释看,应该是HashMap支持的最大长度,如果超过这个值,则使用这个值。</p>
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="java" scheme="http://www.zhishuo.info/tags/java/"/>
<category term="jdk源码" scheme="http://www.zhishuo.info/tags/jdk%E6%BA%90%E7%A0%81/"/>
<category term="hashMap" scheme="http://www.zhishuo.info/tags/hashMap/"/>
<category term="map" scheme="http://www.zhishuo.info/tags/map/"/>
</entry>
<entry>
<title>JDK源码之CopyOnWriteArrayList</title>
<link href="http://www.zhishuo.info/posts/java/JDK%E6%BA%90%E7%A0%81%E4%B9%8BCopyOnWriteArrayList.html"/>
<id>http://www.zhishuo.info/posts/java/JDK源码之CopyOnWriteArrayList.html</id>
<published>2016-11-10T15:14:52.000Z</published>
<updated>2016-11-16T01:50:25.000Z</updated>
<content type="html"><![CDATA[<h4 id="什么是CopyOnWriteArrayList"><a href="#什么是CopyOnWriteArrayList" class="headerlink" title="什么是CopyOnWriteArrayList"></a>什么是CopyOnWriteArrayList</h4><p>CopyOnWriteArrayList是一个线程安全的List,和它相同的还有这几个,Vectory,SynchronizedList。<br>它们都是实现了List接口的线程安全类。</p>
<p>CopyOnWriteArrayList使用的是一种写时复制的算法,也就是说在执行add方法的时候,并不像传统的ArrayList一样在当前数组直接操作,<br>而是在执行add方法的时候,会把原来的数组复制并且长度+1,然后把新值设置到新的数组中,然后把新数组设置为当前使用状态,由于数组是<code>volatile</code>修饰的,所以JVM会自动来保证数组的可见性。</p>
<p>这样使此List在读取时可以不用加锁,提高读取效率,但是在添加的时候效率会很低下。所以我感觉CopyOnWriteArrayList的使用场景就是读多写少的场景,甚至在尽可能的情况下,不去写。这样才能发挥此数据结构的最大优点。<br><a id="more"></a></p>
<h4 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/** The lock protecting all mutators */</span></div><div class="line"><span class="keyword">final</span> <span class="keyword">transient</span> ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</div><div class="line"><span class="comment">/** The array, accessed only via getArray/setArray. */</span></div><div class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Object[] array;</div></pre></td></tr></table></figure>
<p>数组结构和可重入锁,由于数组是<code>volatile</code>修饰的,所以JVM会利用CPU的缓存失效功能,将对象保持强一致性。假设有4个CPU,因为每个CPU都有属于自己的高速缓存,在此类对象进行更改时,JVM会调用CPU方法,将所有CPU的缓存失效,并且把修改后的内容刷回主存来保持此对象的强一致性,不知道这里我理解的有没有问题,如果有问题请各位指正。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Creates an empty list.</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">()</span> </span>{</div><div class="line"> setArray(<span class="keyword">new</span> Object[<span class="number">0</span>]);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>默认初始化,是一个0长度的数组。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Appends the specified element to the end of this list.</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> e element to be appended to this list</div><div class="line"> * <span class="doctag">@return</span> {<span class="doctag">@code</span> true} (as specified by {<span class="doctag">@link</span> Collection#add})</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>{</div><div class="line"> <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</div><div class="line"> lock.lock();</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> Object[] elements = getArray();</div><div class="line"> <span class="keyword">int</span> len = elements.length;</div><div class="line"> Object[] newElements = Arrays.copyOf(elements, len + <span class="number">1</span>);</div><div class="line"> newElements[len] = e;</div><div class="line"> setArray(newElements);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line"> } <span class="keyword">finally</span> {</div><div class="line"> lock.unlock();</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>这里便是添加元素的代码,这里使用了重入锁,重入锁的意思是如果当前线程已经拿到了锁,再次获取此对象上的锁的时候,也会获得到锁不会排队。</p>
<p>上锁以后,先得到当前的数组,并取得当前的长度,然后调用<code>Arrays.copyOf</code>方法,复制数组并且长度+1,然后把新的元素加到新的数组中,设置新的数组为当前使用的数组。返回添加成功,并且释放锁。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Removes the element at the specified position in this list.</div><div class="line"> * Shifts any subsequent elements to the left (subtracts one from their</div><div class="line"> * indices). Returns the element that was removed from the list.</div><div class="line"> *</div><div class="line"> * <span class="doctag">@throws</span> IndexOutOfBoundsException {<span class="doctag">@inheritDoc</span>}</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>{</div><div class="line"> <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</div><div class="line"> lock.lock();</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> Object[] elements = getArray();</div><div class="line"> <span class="keyword">int</span> len = elements.length;</div><div class="line"> E oldValue = get(elements, index);</div><div class="line"> <span class="keyword">int</span> numMoved = len - index - <span class="number">1</span>;</div><div class="line"> <span class="keyword">if</span> (numMoved == <span class="number">0</span>)</div><div class="line"> setArray(Arrays.copyOf(elements, len - <span class="number">1</span>));</div><div class="line"> <span class="keyword">else</span> {</div><div class="line"> Object[] newElements = <span class="keyword">new</span> Object[len - <span class="number">1</span>];</div><div class="line"> System.arraycopy(elements, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</div><div class="line"> System.arraycopy(elements, index + <span class="number">1</span>, newElements, index,</div><div class="line"> numMoved);</div><div class="line"> setArray(newElements);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> oldValue;</div><div class="line"> } <span class="keyword">finally</span> {</div><div class="line"> lock.unlock();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>删除这里基本同增加一样,也是先加锁,然后再操作。</p>
<p>这里不同的是使用了<code>int numMoved = len - index - 1;</code>这个值来判断删除的元素是不是最后一个元素,如果是最后一个元素也就是<code>numMoved=0</code>,直接把原来的数组复制长度-1即可。</p>
<p>如果不是最后一个元素,则是分开复制的,先复制删除元素前面的数据,再复制删除元素后面的数据,最后形成新的数组,设置并返回,最后解锁。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Replaces the element at the specified position in this list with the</div><div class="line"> * specified element.</div><div class="line"> *</div><div class="line"> * <span class="doctag">@throws</span> IndexOutOfBoundsException {<span class="doctag">@inheritDoc</span>}</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> E <span class="title">set</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>{</div><div class="line"> <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</div><div class="line"> lock.lock();</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> Object[] elements = getArray();</div><div class="line"> E oldValue = get(elements, index);</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (oldValue != element) {</div><div class="line"> <span class="keyword">int</span> len = elements.length;</div><div class="line"> Object[] newElements = Arrays.copyOf(elements, len);</div><div class="line"> newElements[index] = element;</div><div class="line"> setArray(newElements);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">// Not quite a no-op; ensures volatile write semantics</span></div><div class="line"> setArray(elements);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> oldValue;</div><div class="line"> } <span class="keyword">finally</span> {</div><div class="line"> lock.unlock();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>修改操作也类似,先加锁,得到原值,如果原值和修改的元素相等,其实感觉什么也没干,看这里注释只是为了保持volatile的语法。如果不等的话拷贝一个新的数组,在新的数组上修改值,把新的数组设置为当前使用的数组,返回修改之前的元素,解锁,完成。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * {<span class="doctag">@inheritDoc</span>}</div><div class="line"> *</div><div class="line"> * <span class="doctag">@throws</span> IndexOutOfBoundsException {<span class="doctag">@inheritDoc</span>}</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>{</div><div class="line"> <span class="keyword">return</span> get(getArray(), index);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>下标查找,这里看到和普通的数组没什么区别,就是普通的下标查找。</p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>从源码来看,CopyOnWriteArrayList增,删,改,都会复制一下新的数组然后再设置回去,所以强烈建议此数据结构的使用场景基本是只读的,否则在大量操作的情况下性能应该会很慢(待以后数据验证)。</p>
]]></content>
<summary type="html">
<h4 id="什么是CopyOnWriteArrayList"><a href="#什么是CopyOnWriteArrayList" class="headerlink" title="什么是CopyOnWriteArrayList"></a>什么是CopyOnWriteArrayList</h4><p>CopyOnWriteArrayList是一个线程安全的List,和它相同的还有这几个,Vectory,SynchronizedList。<br>它们都是实现了List接口的线程安全类。</p>
<p>CopyOnWriteArrayList使用的是一种写时复制的算法,也就是说在执行add方法的时候,并不像传统的ArrayList一样在当前数组直接操作,<br>而是在执行add方法的时候,会把原来的数组复制并且长度+1,然后把新值设置到新的数组中,然后把新数组设置为当前使用状态,由于数组是<code>volatile</code>修饰的,所以JVM会自动来保证数组的可见性。</p>
<p>这样使此List在读取时可以不用加锁,提高读取效率,但是在添加的时候效率会很低下。所以我感觉CopyOnWriteArrayList的使用场景就是读多写少的场景,甚至在尽可能的情况下,不去写。这样才能发挥此数据结构的最大优点。<br>
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="java" scheme="http://www.zhishuo.info/tags/java/"/>
<category term="jdk" scheme="http://www.zhishuo.info/tags/jdk/"/>
<category term="jdk源码" scheme="http://www.zhishuo.info/tags/jdk%E6%BA%90%E7%A0%81/"/>
<category term="CopyOnWriteArrayList" scheme="http://www.zhishuo.info/tags/CopyOnWriteArrayList/"/>
</entry>
<entry>
<title>hexo next主题添加LeanCloud统计功能</title>
<link href="http://www.zhishuo.info/posts/hexo/hexo-next%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0LeanCloud%E7%BB%9F%E8%AE%A1%E5%8A%9F%E8%83%BD.html"/>
<id>http://www.zhishuo.info/posts/hexo/hexo-next主题添加LeanCloud统计功能.html</id>
<published>2016-11-10T12:51:47.000Z</published>
<updated>2016-11-15T16:16:29.000Z</updated>
<content type="html"><![CDATA[<h4 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h4><p>网上能查到很多next主题添加LeanCloud主题的方法,但我看都是说在站点的<code>_config.yml</code>中添加<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">leancloud_visitors:</div><div class="line"> enable: true</div><div class="line"> app_id: appid</div><div class="line"> app_key: key</div></pre></td></tr></table></figure></p>
<p>与是我也在站点的<code>_config.yml</code>中添加了,但不起作用。<br>与是我又去主题的目录中添加了,报错。<br>最后找到原因,是因为主题的<code>_config.yml</code>配置文件中已经自带了,在主题的<code>_config.yml</code>配置文件中308行左右,在这里直接配置即可。</p>
<h4 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h4><p><a href="https://notes.wanghao.work/2015-10-21-%E4%B8%BANexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0%E6%96%87%E7%AB%A0%E9%98%85%E8%AF%BB%E9%87%8F%E7%BB%9F%E8%AE%A1%E5%8A%9F%E8%83%BD.html" target="_blank" rel="external">next主题添加LeanCloud</a></p>
]]></content>
<summary type="html">
<h4 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h4><p>网上能查到很多next主题添加LeanCloud主题的方法,但我看都是说在站点的<code>_config.yml<
</summary>
<category term="hexo" scheme="http://www.zhishuo.info/categories/hexo/"/>
<category term="hexo" scheme="http://www.zhishuo.info/tags/hexo/"/>
<category term="next" scheme="http://www.zhishuo.info/tags/next/"/>
<category term="leancloud" scheme="http://www.zhishuo.info/tags/leancloud/"/>
</entry>
<entry>
<title>mac下使用springli创建springboot应用</title>
<link href="http://www.zhishuo.info/posts/java/mac%E4%B8%8B%E4%BD%BF%E7%94%A8springli%E5%88%9B%E5%BB%BAspringboot%E5%BA%94%E7%94%A8.html"/>
<id>http://www.zhishuo.info/posts/java/mac下使用springli创建springboot应用.html</id>
<published>2016-11-04T07:53:51.000Z</published>
<updated>2016-11-04T08:06:35.000Z</updated>
<content type="html"><![CDATA[<h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><p>生成springboot有以下几种方式</p>
<ol>
<li>maven</li>
<li>gradle</li>
<li>springli</li>
<li>spring官网可以点击生成脚手架</li>
</ol>
<p>spring官网介绍说springli是最快的方式,所以我使用了springli来构建。</p>
<p>mac安装springli我使用的是<code>brew</code>安装,如果没使用过<code>brew</code>请移步<a href="http://brew.sh/" target="_blank" rel="external">brew</a>.</p>
<p>brew安装springli<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ brew tap pivotal/tap</div><div class="line">$ brew install springboot</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><p>生成springboot有以下几种方式</p>
<ol>
<li>maven</li>
<li>gradle</li>
<l
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="java" scheme="http://www.zhishuo.info/tags/java/"/>
<category term="springboot" scheme="http://www.zhishuo.info/tags/springboot/"/>
</entry>
<entry>
<title>使用hexo生成博客在github js css 404解决方案</title>
<link href="http://www.zhishuo.info/posts/hexo/%E4%BD%BF%E7%94%A8hexo%E7%94%9F%E6%88%90%E5%8D%9A%E5%AE%A2%E5%9C%A8github-js-css-404%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.html"/>
<id>http://www.zhishuo.info/posts/hexo/使用hexo生成博客在github-js-css-404解决方案.html</id>
<published>2016-11-04T05:32:58.000Z</published>
<updated>2016-11-15T16:17:18.000Z</updated>
<content type="html"><![CDATA[<h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>博主最近又开始写博客了,发现使用hexo next 主题,上传到github上之后,所有的vendors文件夹下的资源访问404,博主查了一些资料没有解决,故给github官方写了一封邮件,官方这样回复</p>
<blockquote>
<p>Thanks for reaching out! We recently updated to Jekyll v3.3, which ignores the vendor folder by default. If you’re not using Jekyll, you can add a .nojekyll file to the root of your repository to disable Jekyll from building your site. Once you do that, your site should build with your vendor folder.</p>
</blockquote>
<p>所以根据提示,在要目录下建立.nojekyll文件即可恢复正常,另外在hexo里,如果把文件放在source目录下,不会被生成到public目录中,根据github上网友的提示,把.nojekyll文件放在hexo主目录.deploy_git/ 文件夹下即可正常使用hexo d 上传,并且此文件也会上传到根目录。</p>
<h3 id="2016-11-7号更新"><a href="#2016-11-7号更新" class="headerlink" title="2016-11-7号更新"></a>2016-11-7号更新</h3><p>next主题源码更新了,把vendors目录改名了,更新为最新代码即可解决。</p>
]]></content>
<summary type="html">
<h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>博主最近又开始写博客了,发现使用hexo next 主题,上传到github上之后,所有的vendors文件夹下的资源访问404,博主查了一
</summary>
<category term="hexo" scheme="http://www.zhishuo.info/categories/hexo/"/>
<category term="hexo" scheme="http://www.zhishuo.info/tags/hexo/"/>
</entry>
<entry>
<title>读spring源码深度解析(二)</title>
<link href="http://www.zhishuo.info/posts/java/%E8%AF%BBspring%E6%BA%90%E7%A0%81%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90(%E4%BA%8C).html"/>
<id>http://www.zhishuo.info/posts/java/读spring源码深度解析(二).html</id>
<published>2016-03-09T09:16:21.000Z</published>
<updated>2016-11-15T14:51:46.000Z</updated>
<content type="html"><![CDATA[<p><strong><em>不明白</em></strong><br><code>DefaultListableBeanFactory.java</code>中使用了<code>AccessController.doPrivileged()</code>方法,个人理解此方法好像是以当前上下文执行一些代码。<br><code>javaUtilOptionalClass</code>这个属性,是用了JDK8的<code>java.util.Optional</code>,此类是用来避免在Java中出现各种null的问题,但不知道为什么是写在了静态代码块中,而且是以动态加载的方式来加载的。</p>
<p><strong><em>初始类</em></strong><br>从<code>DefaultListableBeanFactory.java</code>类的getBean()中,开始对整个工厂类进分析。</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/DefaultListableBeanFactory.png" alt="类的关系调用图"></p>
<p>静态导入,如果你多次用到某个工具类的静态方法,可以使用静态导入,这样使代码更整洁美观。<br><code>SimpleAliasRegistry.java</code>这个类实现了对Alias的一些操作。<br><code>BeanFactory.java</code>接口定义一些对Bean的基本操作。<br><code>DefaultSingletonBeanRegistry.java</code>对spring对Bean的一些操作做了各种封装,创建,消毁,依赖等。<br><code>AbstractAutowireCapableBeanFactory.java</code>对<code>AutowireCapableBeanFactory</code>定义的方法进行了实现,此类<br><code>XmlBeanDefinitionReader.java</code>实现了用xml解析资源文件,并且实现了<code>DefaultListableBeanFactory.java</code>类</p>
<a id="more"></a>
<p>xml如果要使用DTD文件来验证的话,需要在xml文件头中加入<code><!-- DOCTYPE</code></p>
<p>在解析DTD和XSD时,使用的是不同的机制,如果是DTD的话,会直接取到systemid在当前目录下寻找,如果是xsd时,会在META/schemas中找到对应的systemid</p>
<p>在解析xml时,如果设置了ResourceLoader,就使用用户设置的,如果没有设置,就使用默认的ResourceEntityResolver解析,解析Xml是使用的SAX方法解析,解析出来Document之后,会把Document进行解析,进行Bean的注册。</p>
<p>在注册的时候,使用了一个class类的cast方法,感觉这个方法使用的比较好,以前没有见过这种强制转换类型的使用,使用的<code>DefaultBeanDefinitionDocumentReader.java</code>来注册Bean,这里注册Bean的那些Map,是使用的<code>SimpleBeanDefinitionRegistry.java</code>类,如果没有指定应该是<code>DefaultSingletonBeanRegistry.java</code>?,注册的时候还会返回此次注册的类的数量。<br>创建这个对象XmlReaderContext不知道是什么作用,里面是对XmlBeanDefinitionReader的一些封装,解析的时候还留了一前一后,供子类自己实现。<code>DefaultBeanDefinitionDocumentReader.java</code>中是对xml的解析,先根据标签来,然后再解析其中所有的属性,把xml组装成Java对象<code>GenericBeanDefinition.java</code></p>
<p>解析Bean标签过程,获取id和name,如果没有指定id,就用name做这个类的key<br>检查类名的唯一性<br>判断此xml是否配置了一系列Spring的属性,并且在对应的Java对象中,设置对应的属性。</p>
]]></content>
<summary type="html">
<p><strong><em>不明白</em></strong><br><code>DefaultListableBeanFactory.java</code>中使用了<code>AccessController.doPrivileged()</code>方法,个人理解此方法好像是以当前上下文执行一些代码。<br><code>javaUtilOptionalClass</code>这个属性,是用了JDK8的<code>java.util.Optional</code>,此类是用来避免在Java中出现各种null的问题,但不知道为什么是写在了静态代码块中,而且是以动态加载的方式来加载的。</p>
<p><strong><em>初始类</em></strong><br>从<code>DefaultListableBeanFactory.java</code>类的getBean()中,开始对整个工厂类进分析。</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/DefaultListableBeanFactory.png" alt="类的关系调用图"></p>
<p>静态导入,如果你多次用到某个工具类的静态方法,可以使用静态导入,这样使代码更整洁美观。<br><code>SimpleAliasRegistry.java</code>这个类实现了对Alias的一些操作。<br><code>BeanFactory.java</code>接口定义一些对Bean的基本操作。<br><code>DefaultSingletonBeanRegistry.java</code>对spring对Bean的一些操作做了各种封装,创建,消毁,依赖等。<br><code>AbstractAutowireCapableBeanFactory.java</code>对<code>AutowireCapableBeanFactory</code>定义的方法进行了实现,此类<br><code>XmlBeanDefinitionReader.java</code>实现了用xml解析资源文件,并且实现了<code>DefaultListableBeanFactory.java</code>类</p>
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="spring" scheme="http://www.zhishuo.info/tags/spring/"/>
<category term="spring源码" scheme="http://www.zhishuo.info/tags/spring%E6%BA%90%E7%A0%81/"/>
</entry>
<entry>
<title>阅读ActiveMQ源码总结</title>
<link href="http://www.zhishuo.info/posts/java/%E9%98%85%E8%AF%BBActiveMQ%E6%BA%90%E7%A0%81%E6%80%BB%E7%BB%93.html"/>
<id>http://www.zhishuo.info/posts/java/阅读ActiveMQ源码总结.html</id>
<published>2016-01-07T14:06:48.000Z</published>
<updated>2016-11-15T14:51:07.000Z</updated>
<content type="html"><![CDATA[<h3 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h3><ul>
<li>下载源码<code>git clone https://github.com/apache/activemq.git</code></li>
<li>使用maven编译源码并且下载依赖<code>mvn clean install</code></li>
<li>导入到开发工具eclipse<code>mvn eclipse:eclipse</code> 或者idea <code>mvn idea:idea</code></li>
<li>默认会去maven中央仓库下载jar包,如果下载速度慢可以翻墙或者改成开源中国的镜像仓库<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">mirrors</span>></span></div><div class="line"> <span class="tag"><<span class="name">mirror</span>></span></div><div class="line"> <span class="tag"><<span class="name">id</span>></span>CN<span class="tag"></<span class="name">id</span>></span></div><div class="line"> <span class="tag"><<span class="name">name</span>></span>OSChina Central<span class="tag"></<span class="name">name</span>></span></div><div class="line"> <span class="tag"><<span class="name">url</span>></span>http://maven.oschina.net/content/groups/public/<span class="tag"></<span class="name">url</span>></span></div><div class="line"> <span class="tag"><<span class="name">mirrorOf</span>></span>central<span class="tag"></<span class="name">mirrorOf</span>></span></div><div class="line"> <span class="tag"></<span class="name">mirror</span>></span></div><div class="line"><span class="tag"></<span class="name">mirrors</span>></span></div></pre></td></tr></table></figure>
</li>
</ul>
<a id="more"></a>
<h3 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h3><ul>
<li>idea导入之后无任何显示,事实证明楼主太着急,等编译之后就有了。。</li>
</ul>
<h3 id="客户端启动"><a href="#客户端启动" class="headerlink" title="客户端启动"></a>客户端启动</h3><ul>
<li>java客户端代码我是按照《Java消息服务》这本书中的例子完成的</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div></pre></td><td class="code"><pre><div class="line"> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Chat</span> <span class="keyword">implements</span> <span class="title">javax</span>.<span class="title">jms</span>.<span class="title">MessageListener</span></span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> TopicSession pubSession;</div><div class="line"> <span class="keyword">private</span> TopicPublisher publisher;</div><div class="line"> <span class="keyword">private</span> TopicConnection connection;</div><div class="line"> <span class="keyword">private</span> String username;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <span class="doctag">@param</span> topicFactory</div><div class="line"> * <span class="doctag">@param</span> topicName</div><div class="line"> * <span class="doctag">@param</span> username</div><div class="line"> * <span class="doctag">@throws</span> Exception</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Chat</span><span class="params">(String topicFactory,String topicName,String username)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="comment">//使用JNDI?</span></div><div class="line"> InitialContext ctx = <span class="keyword">new</span> InitialContext();</div><div class="line"> <span class="comment">//拿到工场</span></div><div class="line"> TopicConnectionFactory conFactory = (TopicConnectionFactory)ctx.lookup(topicFactory);</div><div class="line"> <span class="comment">//创建连接</span></div><div class="line"> TopicConnection connection = conFactory.createTopicConnection();</div><div class="line"></div><div class="line"> TopicSession pubSession = connection.createTopicSession(<span class="keyword">false</span>, Session.AUTO_ACKNOWLEDGE);</div><div class="line"> TopicSession subSession = connection.createTopicSession(<span class="keyword">false</span>, Session.AUTO_ACKNOWLEDGE);</div><div class="line"></div><div class="line"> Topic chatTopic = (Topic)ctx.lookup(topicName);</div><div class="line"></div><div class="line"> TopicPublisher publisher = pubSession.createPublisher(chatTopic);</div><div class="line"> String selector = <span class="string">"username=zhishuo"</span>;</div><div class="line"> TopicSubscriber subscriber = subSession.createSubscriber(chatTopic,selector,<span class="keyword">true</span>);</div><div class="line"></div><div class="line"> subscriber.setMessageListener(<span class="keyword">this</span>);</div><div class="line"></div><div class="line"> <span class="keyword">this</span>.connection = connection;</div><div class="line"> <span class="keyword">this</span>.pubSession = pubSession;</div><div class="line"> <span class="keyword">this</span>.publisher = publisher;</div><div class="line"> <span class="keyword">this</span>.username = username;</div><div class="line"> connection.start();</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onMessage</span><span class="params">(Message message)</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> TextMessage textMessage = (TextMessage)message;</div><div class="line"> System.out.println(textMessage.getText());</div><div class="line"> System.out.println(message.getJMSDestination());</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 发送消息</div><div class="line"> * <span class="doctag">@param</span> text</div><div class="line"> * <span class="doctag">@throws</span> JMSException</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">writeMessage</span><span class="params">(String text)</span> <span class="keyword">throws</span> JMSException </span>{</div><div class="line"> TextMessage textMessage = pubSession.createTextMessage(username+<span class="string">":"</span>+text);</div><div class="line"> publisher.publish(textMessage);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> <span class="keyword">throws</span> JMSException </span>{</div><div class="line"> connection.close();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="keyword">if</span>(args.length!=<span class="number">3</span>){</div><div class="line"> System.out.println(<span class="string">"something is missing"</span>);</div><div class="line"> }</div><div class="line"> <span class="comment">// topicFactory, topicName, username</span></div><div class="line"> Chat chat = <span class="keyword">new</span> Chat(args[<span class="number">0</span>],args[<span class="number">1</span>],args[<span class="number">2</span>]);</div><div class="line"> BufferedReader commondLine = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(System.in));</div><div class="line"></div><div class="line"> <span class="keyword">while</span>(<span class="keyword">true</span>){</div><div class="line"> String s = commondLine.readLine();</div><div class="line"> <span class="keyword">if</span>(s.equalsIgnoreCase(<span class="string">"exit"</span>)){</div><div class="line"> chat.clone();</div><div class="line"> System.exit(<span class="number">0</span>);</div><div class="line"> }<span class="keyword">else</span>{</div><div class="line"> chat.writeMessage(s);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<ul>
<li><p>还需要在classpath中加入<code>jndi.properties</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">#配置实现工场</div><div class="line">java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory</div><div class="line">#配置activemq地址</div><div class="line">java.naming.provider.url=tcp://localhost:61616</div><div class="line">#这里是配置的安全机制?</div><div class="line">java.naming.security.principal=system</div><div class="line">java.naming.security.credentials=manager</div><div class="line">#订阅主题的名称,可以以逗号,隔开</div><div class="line">connectionFactoryNames=TopicCF</div><div class="line">#topic</div><div class="line">topic.topic1=jms.topic1</div></pre></td></tr></table></figure>
</li>
<li><p>启动<code>Chart</code>类main方法时,传入<code>TopicCF topic1 zhishuo</code>这样客户端就可以正常启动,<br>并且发送消息了,如果启动多个客户端,然后按回车发送消息,所有的人都可以看到。</p>
</li>
</ul>
<h3 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h3><ul>
<li><code>InitialContext ctx = new InitialContext();</code><br>这里是JMS给出的公用调用方法,new对象时调用了默认的init()方法。<br><code>myProps = (Hashtable<Object,Object>) ResourceManager.getInitialEnvironment(environment);</code>里面主要是去加载当前环境变量<br>和一些JVM设置的环境变量参数。</li>
<li>主要初始化都在<code>getDefaultInitCtx()--->NamingManager.getInitialContext(myProps);</code><br>中完成,会在环境变量配置参数中找到<code>String INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial";</code><br>key值,由于我们这里配置的是<code>java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory</code>,所以实例化的也就是<code>ActiveMQInitialcontextFactory</code>,最后再调用<code>ActiveMQInitialcontextFactory.getInitialContext(env)</code>这里传过去的参数是环境变量。</li>
<li>我们来到<code>ActiveMQInitialcontextFactory.getInitialContext(env)</code>方法中,此方法便是真正用来初始化队列,<br>主题<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> Context <span class="title">getInitialContext</span><span class="params">(Hashtable environment)</span> <span class="keyword">throws</span> NamingException </span>{</div><div class="line"> <span class="comment">// lets create a factory</span></div><div class="line"> Map<String, Object> data = <span class="keyword">new</span> ConcurrentHashMap<String, Object>();</div><div class="line"> String[] names = getConnectionFactoryNames(environment);</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < names.length; i++) {</div><div class="line"> ActiveMQConnectionFactory factory = <span class="keyword">null</span>;</div><div class="line"> String name = names[i];</div><div class="line"></div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> factory = createConnectionFactory(name, environment);</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NamingException(<span class="string">"Invalid broker URL"</span>);</div><div class="line"></div><div class="line"> }</div><div class="line"> <span class="comment">/*</span></div><div class="line"> * if( broker==null ) { try { broker = factory.getEmbeddedBroker(); }</div><div class="line"> * catch (JMSException e) { log.warn("Failed to get embedded</div><div class="line"> * broker", e); } }</div><div class="line"> */</div><div class="line"> data.put(name, factory); <span class="comment">// 实际上是 tocipIF ActiveMQConnectionFactory</span></div><div class="line"> }</div><div class="line"></div><div class="line"> createQueues(data, environment);</div><div class="line"> createTopics(data, environment);</div><div class="line"> <span class="comment">/*</span></div><div class="line"> * if (broker != null) { data.put("destinations",</div><div class="line"> * broker.getDestinationContext(environment)); }</div><div class="line"> */</div><div class="line"> data.put(<span class="string">"dynamicQueues"</span>, <span class="keyword">new</span> LazyCreateContext() {</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">6503881346214855588L</span>;</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> Object <span class="title">createEntry</span><span class="params">(String name)</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ActiveMQQueue(name);</div><div class="line"> }</div><div class="line"> });</div><div class="line"> data.put(<span class="string">"dynamicTopics"</span>, <span class="keyword">new</span> LazyCreateContext() {</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">2019166796234979615L</span>;</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> Object <span class="title">createEntry</span><span class="params">(String name)</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ActiveMQTopic(name);</div><div class="line"> }</div><div class="line"> });</div><div class="line"></div><div class="line"> <span class="keyword">return</span> createContext(environment, data);</div><div class="line"> }</div></pre></td></tr></table></figure>
</li>
</ul>
<p><code>getConnectionFactoryNames</code>方法中,是为了遍历并且初始化<code>connectionFactoryNames</code>配置的Value值,我们这里配置的<code>connectionFactoryNames=TopicCF</code>,这里用到了<code>StringTokenizer</code>感觉很好用,平时自己都是用string的split,没想到还有这种用法。<br>然后遍历刚才解析的Value值,并且最终创建<code>ActiveMQConnectionFactory</code>对象返回。<br>把创建好的对像放入Hashmap中,map.put(TopicCF,ActiveMQConnectionFactory),</p>
<blockquote>
<p>failover://tcp://localhost:61616 这是此对象初始化时url属性的默认值<br>初始化的时候修改成了tcp://localhost:61616</p>
</blockquote>
<p>这里是每一个value值都对应一个自己的工厂类。</p>
<p>接下来创建Queues和Topics查找配置文件中以queue.和topic.开头的,并且生成ActiveMQQueue对象。<br>我们这里配置的<code>jms.topic1</code>最后也放入map.put(topic1,ActiveMQTopic)。</p>
<p>接下来创建了动态的队列和主题,不知何用。<br>dynamicQueues<br>dynamicTopics<br>并且都放入了map<br>最后创建了ReadOnlyContext对象,并且把相关的数据绑定都传入其中。<br>至此第一句代码初始化完成。</p>
<ul>
<li><p><code>(TopicConnectionFactory)ctx.lookup(topicFactory)</code>这里是去加载实现工厂的类,由于上一个初始化最终返回的是<br>ReadOnlyContext对象,所以这里调用的是ReadOnlyContext.lookup方法,我们来看<code>public Object lookup(String name) throws NamingException {</code>方法,这里其实就是根据传入的名称,取出相应的工厂,由于我们在启动时传入的工厂类名是<br>TopicCF 所以,这里就是取出刚才放入的ActiveMQConnectionFactory工厂类,类图如下<br><img src="http://ogflhfadi.bkt.clouddn.com/ActiveMQConnectionFactory%E7%B1%BB%E5%9B%BE.png" alt="类图"></p>
</li>
<li><p><code>conFactory.createTopicConnection()</code>,这里是调用的ActiveMQConnectionFactory中的方法,该类中初始化了传输协议模式</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (scheme.equals(<span class="string">"auto"</span>)) {</div><div class="line"> connectBrokerUL = <span class="keyword">new</span> URI(brokerURL.toString().replace(<span class="string">"auto"</span>, <span class="string">"tcp"</span>));</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (scheme.equals(<span class="string">"auto+ssl"</span>)) {</div><div class="line"> connectBrokerUL = <span class="keyword">new</span> URI(brokerURL.toString().replace(<span class="string">"auto+ssl"</span>, <span class="string">"ssl"</span>));</div><div class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (scheme.equals(<span class="string">"auto+nio"</span>)) {</div><div class="line"> connectBrokerUL = <span class="keyword">new</span> URI(brokerURL.toString().replace(<span class="string">"auto+nio"</span>, <span class="string">"nio"</span>));</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (scheme.equals(<span class="string">"auto+nio+ssl"</span>)) {</div><div class="line"> connectBrokerUL = <span class="keyword">new</span> URI(brokerURL.toString().replace(<span class="string">"auto+nio+ssl"</span>, <span class="string">"nio+ssl"</span>));</div><div class="line"> }</div></pre></td></tr></table></figure>
</li>
</ul>
<p>如果以上都没有配置,默认使用TCP协议传输,在<code>TransportFactory.connect()-->findTransportFactory()</code>中,查找资源对应的配置协议,如果没有查到,使用默认的在<br><code>private static final FactoryFinder TRANSPORT_FACTORY_FINDER = new FactoryFinder("META-INF/services/org/apache/activemq/transport/");</code>此文件目录中,因为这里是用的<code>tcp://localhost:61616</code>所以这里使用的就是<code>META-INF/services/org/apache/activemq/transport/tcp</code>文件,此文件中实际配置的是TCP的传输器<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">class=org.apache.activemq.transport.tcp.TcpTransportFactory</div></pre></td></tr></table></figure></p>
<p>然后调TcpTransportFactory.doConnect()进行连接,至此传输器创建完成。</p>
<ul>
<li>接下来创建ActiveMQConnection连接</li>
<li><p>最后启动连接,并且设置一些默认值,至此创建连接完成,并返回连接。</p>
</li>
<li><p><code>TopicSession pubSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);</code>创建发布和订阅Session,<br>最终调用的是ActiveMQConnection.createTopicSession(),创建ActiveMQSession对像并返回。</p>
</li>
<li><p><code>Topic chatTopic = (Topic)ctx.lookup(topicName);</code>加载主题,这里的值为<code>topic1</code>,<br>因为在前边初始化的时候已经放入了对像,所以这里的值为<code>ActiveMQTopic</code>,调用ActiveMQSession.createPublisher() 和 createSubscriber() 创建发布者ActiveMQTopicPublisher和消费者ActiveMQTopicSubscriber。</p>
</li>
<li><p>创建完成之后,把自己设置为订阅者的监听器,最终启动连接,客户端启动代码分析完成,因为有一些自己也不是很理解,所以未能表达。</p>
</li>
</ul>
<h3 id="服务端启动"><a href="#服务端启动" class="headerlink" title="服务端启动"></a>服务端启动</h3><ul>
<li>以下是服务端启动的类图调用关系:<br><img src="http://ogflhfadi.bkt.clouddn.com/active_server_start_seq.png" alt="启动类图"></li>
</ul>
<p><em>这里还了解了java -D 是可以把一些参数设置到JVM中的</em></p>
<p>服务端启动这里充分体现了命令设计模式的案例,我认为是很好的学习例子,最终调用的是StartCommand.runTask()方法,类中有如下几个重要功能</p>
<ul>
<li>初始化broker</li>
<li>添加JVM shutdownhook 停止时进行清理</li>
</ul>
<p>下面来看<code>broker = BrokerFactory.createBroker(configURI);</code>此类中在创建工厂时也使用了文件配置的方法由于默认是使用的<code>xbean:activemq.xml</code>资源文件,所以在创建工厂时就是实例化的<br><code>META-INF/services/org/apache/activemq/broker/</code>xbean文件中配置的类,<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">class=org.apache.activemq.xbean.XBeanBrokerFactory</div></pre></td></tr></table></figure></p>
<p>实际调用的是<code>XBeanBrokerFactory.createBroker()</code>此类中创建了Spring的<code>ResourceXmlApplicationContext</code>实现对象,但不知这里为何要这样。这里最后还调用了spring框架中refresh方法,此方法会初始化spring整个框架。<br>最终创建一个BrokerService返回,broker中包含了所有上下文环境。</p>
<p>KahaDBPersistenceAdapter 调用关系图<br><img src="http://ogflhfadi.bkt.clouddn.com/active_db_ref.png" alt="调用关系"></p>
<p>KahaDBPersistenceAdapter 用例图</p>
<p><img src="http://ogflhfadi.bkt.clouddn.com/active_server_db_ref.png" alt="调用关系"><br>最后启动,至此服务启动完成。</p>
<h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3>]]></content>
<summary type="html">
<h3 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h3><ul>
<li>下载源码<code>git clone https://github.com/apache/activemq.git</code></li>
<li>使用maven编译源码并且下载依赖<code>mvn clean install</code></li>
<li>导入到开发工具eclipse<code>mvn eclipse:eclipse</code> 或者idea <code>mvn idea:idea</code></li>
<li>默认会去maven中央仓库下载jar包,如果下载速度慢可以翻墙或者改成开源中国的镜像仓库<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">mirrors</span>&gt;</span></div><div class="line"> <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></div><div class="line"> <span class="tag">&lt;<span class="name">id</span>&gt;</span>CN<span class="tag">&lt;/<span class="name">id</span>&gt;</span></div><div class="line"> <span class="tag">&lt;<span class="name">name</span>&gt;</span>OSChina Central<span class="tag">&lt;/<span class="name">name</span>&gt;</span></div><div class="line"> <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://maven.oschina.net/content/groups/public/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></div><div class="line"> <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></div><div class="line"> <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">mirrors</span>&gt;</span></div></pre></td></tr></table></figure>
</li>
</ul>
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="java" scheme="http://www.zhishuo.info/tags/java/"/>
<category term="ActiveMQ" scheme="http://www.zhishuo.info/tags/ActiveMQ/"/>
</entry>
<entry>
<title>archLinux安装遇到的问题</title>
<link href="http://www.zhishuo.info/posts/linux/archLinux%E5%AE%89%E8%A3%85%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98.html"/>
<id>http://www.zhishuo.info/posts/linux/archLinux安装遇到的问题.html</id>
<published>2015-12-24T12:25:32.000Z</published>
<updated>2016-11-27T10:47:45.000Z</updated>
<content type="html"><![CDATA[<ul>
<li>pacman -Syy 更新之后才能用</li>
<li>启动ssh服务需要安装openssh,<code>pacman -S openssh</code></li>
<li>启动sshd服务<code>systemctl start sshd</code></li>
<li>设置为开机启动服务<code>systemctl enable sshd.service</code></li>
<li>安装vim之后,显示libncursesw.so.6不能找到,运行<code>pacman -Syu</code>升级即可<a id="more"></a>
</li>
</ul>
]]></content>
<summary type="html">
<ul>
<li>pacman -Syy 更新之后才能用</li>
<li>启动ssh服务需要安装openssh,<code>pacman -S openssh</code></li>
<li>启动sshd服务<code>systemctl start sshd</code></li>
<li>设置为开机启动服务<code>systemctl enable sshd.service</code></li>
<li>安装vim之后,显示libncursesw.so.6不能找到,运行<code>pacman -Syu</code>升级即可
</summary>
<category term="linux" scheme="http://www.zhishuo.info/categories/linux/"/>
<category term="linux" scheme="http://www.zhishuo.info/tags/linux/"/>
<category term="arch" scheme="http://www.zhishuo.info/tags/arch/"/>
</entry>
<entry>
<title>读activeMQ源码(一)</title>
<link href="http://www.zhishuo.info/posts/java/%E8%AF%BBactiveMQ%E6%BA%90%E7%A0%81(%E4%B8%80).html"/>
<id>http://www.zhishuo.info/posts/java/读activeMQ源码(一).html</id>
<published>2015-12-08T16:54:49.000Z</published>
<updated>2016-11-03T08:10:39.000Z</updated>
<content type="html"><![CDATA[<p>最近的学习到了消息的阶段,查了下网上的资料,并没有很多具体介绍某一个消息中间件的源码是如何实现,帮自己下了源码想分析下去,希望能坚持。</p>
<p>源码<a href="https://github.com/apache/activemq.git" target="_blank" rel="external">GIT下载地址</a></p>
<p>下载完之后如果要导入eclipse 执行mvn eclipse:eclipse 或者导入IDEA mvn idea:idea</p>
<p>说下个人对rpc和消息的理解:</p>
<ul>
<li>这2种方式从架构上就是不同的,rpc一般都是同步的,消息都是异步的,用在不同的场景上。</li>
</ul>
]]></content>
<summary type="html">
<p>最近的学习到了消息的阶段,查了下网上的资料,并没有很多具体介绍某一个消息中间件的源码是如何实现,帮自己下了源码想分析下去,希望能坚持。</p>
<p>源码<a href="https://github.com/apache/activemq.git" target="_bl
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="java" scheme="http://www.zhishuo.info/tags/java/"/>
<category term="activemq" scheme="http://www.zhishuo.info/tags/activemq/"/>
</entry>
<entry>
<title>mac树莓派安装kaliLinux</title>
<link href="http://www.zhishuo.info/posts/%E6%A0%91%E8%8E%93%E6%B4%BE/mac%E6%A0%91%E8%8E%93%E6%B4%BE%E5%AE%89%E8%A3%85kaliLinux.html"/>
<id>http://www.zhishuo.info/posts/树莓派/mac树莓派安装kaliLinux.html</id>
<published>2015-12-04T13:13:51.000Z</published>
<updated>2016-11-27T10:49:10.000Z</updated>
<content type="html"><![CDATA[<p>树莓派到手大概有一周了,想折腾下kali linux,记录下安装步骤。</p>
<p>准备工作,必须品:</p>
<ol>
<li>树莓派板子一个</li>
<li>5V电源一个</li>
<li>8G SD卡一张</li>
<li>kali linux img <a href="https://www.offensive-security.com/kali-linux-vmware-arm-image-download/" target="_blank" rel="external">下载地址</a> 选择 “RaspberryPi 2” 版本即可</li>
<li>无线网卡或者网线</li>
<li>显示屏</li>
</ol>
<h3 id="windows安装"><a href="#windows安装" class="headerlink" title="windows安装"></a>windows安装</h3><p>怎样在Windows下将Kali安装到SD卡上</p>
<ul>
<li>下载普通版的树莓派专用Kali Linux,并解压img。</li>
<li>下载名为Win32DiskImager的压缩包并解压其中exe后缀的软件。<br>将SD卡插入你的PC中(记得用读卡器)。</li>
<li>双击打开Win32DiskImager.exe,如果你用的是WIN7或WIN8,则需要点击鼠标右键并选择“以管理员身份运行”。</li>
<li>如果该软件无法自动侦测到你的SD卡,你就要在右上角的下拉菜单中找到SD卡并手动选择它。</li>
<li>在软件的图像文件部分点击小文件夹的图标,找到你刚刚下载的Raspbian.img文件。</li>
<li>点击“写入 or write”按键,Win32DiskImager就会帮你完成其它步骤。安装过程结束后,你就可以拔出SD卡然后将其插入树莓派了。</li>
</ul>
<a id="more"></a>
<h3 id="mac-or-linux-安装"><a href="#mac-or-linux-安装" class="headerlink" title="mac or linux 安装"></a>mac or linux 安装</h3><p>怎样在OSX下将Kali安装到SD卡上</p>
<ul>
<li>下载普通版的树莓派专用Kali Linux,并解压img。</li>
<li>SD卡插入电脑</li>
<li>使用<code>df -h</code> 命令查看你插入的SD卡,一般类似 <code>/dev/disk0s4</code> 每个人的机器数字是不一样的</li>
<li>使用<code>diskutil list</code>查看SD卡的名字,一般类似 <code>/dev/disk4</code></li>
<li>然后使用<code>diskutil unmount /dev/disk0s4</code> 命令,卸载已经挂载的SD卡</li>
<li>使用<code>diskutil list</code>查看是否已经卸载,并使用<code>sudo dd bs=4m if=kali-linux.img(这里是你解压img的名字) of=/dev/rdisk2(这里是你SD卡的位置)</code></li>
<li>写入完成就可以了,然后执行<code>diskutil unmountDisk /dev/disk2</code>推出SD卡。!!!这一步一定要执行,否则可能不会正常开机。你就可以拔出SD卡然后将其插入树莓派了。</li>
</ul>
<h3 id="树莓派开机"><a href="#树莓派开机" class="headerlink" title="树莓派开机"></a>树莓派开机</h3><ul>
<li>插入SD卡后可正常开机,kali linux 默认用户名密码 root toor 一定要自行修改,否则很可能会被人猜出来的。</li>
</ul>
<h3 id="说下坑"><a href="#说下坑" class="headerlink" title="说下坑"></a>说下坑</h3><ul>
<li>我查了网上各种资料,都没有给出IMG的下载地址,官网的armhf不能用好么,查了查文档也没找到在哪写的armhf和armel的区别。最终使用了上面我给出的下载地址,下完之后才可以用。</li>
<li>还有,下载之后要验证一下和官网的验证值是否一致,否则有可能自己就成了老黑客的肉鸡,验证方式下载网站已经给出。</li>
</ul>
]]></content>
<summary type="html">
<p>树莓派到手大概有一周了,想折腾下kali linux,记录下安装步骤。</p>
<p>准备工作,必须品:</p>
<ol>
<li>树莓派板子一个</li>
<li>5V电源一个</li>
<li>8G SD卡一张</li>
<li>kali linux img <a href="https://www.offensive-security.com/kali-linux-vmware-arm-image-download/">下载地址</a> 选择 “RaspberryPi 2” 版本即可</li>
<li>无线网卡或者网线</li>
<li>显示屏</li>
</ol>
<h3 id="windows安装"><a href="#windows安装" class="headerlink" title="windows安装"></a>windows安装</h3><p>怎样在Windows下将Kali安装到SD卡上</p>
<ul>
<li>下载普通版的树莓派专用Kali Linux,并解压img。</li>
<li>下载名为Win32DiskImager的压缩包并解压其中exe后缀的软件。<br>将SD卡插入你的PC中(记得用读卡器)。</li>
<li>双击打开Win32DiskImager.exe,如果你用的是WIN7或WIN8,则需要点击鼠标右键并选择“以管理员身份运行”。</li>
<li>如果该软件无法自动侦测到你的SD卡,你就要在右上角的下拉菜单中找到SD卡并手动选择它。</li>
<li>在软件的图像文件部分点击小文件夹的图标,找到你刚刚下载的Raspbian.img文件。</li>
<li>点击“写入 or write”按键,Win32DiskImager就会帮你完成其它步骤。安装过程结束后,你就可以拔出SD卡然后将其插入树莓派了。</li>
</ul>
</summary>
<category term="树莓派" scheme="http://www.zhishuo.info/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
<category term="linux" scheme="http://www.zhishuo.info/tags/linux/"/>
<category term="树莓派" scheme="http://www.zhishuo.info/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
<category term="kali" scheme="http://www.zhishuo.info/tags/kali/"/>
<category term="mac" scheme="http://www.zhishuo.info/tags/mac/"/>
</entry>
<entry>
<title>树莓派ntp时间同步问题</title>
<link href="http://www.zhishuo.info/posts/%E6%A0%91%E8%8E%93%E6%B4%BE/%E6%A0%91%E8%8E%93%E6%B4%BEntp%E6%97%B6%E9%97%B4%E5%90%8C%E6%AD%A5%E9%97%AE%E9%A2%98.html"/>
<id>http://www.zhishuo.info/posts/树莓派/树莓派ntp时间同步问题.html</id>
<published>2015-11-30T14:48:37.000Z</published>
<updated>2016-11-03T08:10:39.000Z</updated>
<content type="html"><![CDATA[<p>今天刚到手树莓派,看了下时间,于是上网搜了下时间同步的问题。<br>然而我按照网上修改的同步服务器并不能解决问题,于是自己试着改了一下。</p>
<p>前提:你要选择正确了时区,运行<code>sudo raspi-config</code> 选择第4项,回车,继续选择第2个,回车,然后选择Aisa,回车,再次选择重庆或者上海,这样时区就调整好了。<br>网上大部分都说在<code>/etc/ntpd.conf</code> 下</p>
<p><code>server 0.debain.pool.ntp.org iburst</code></p>
<p><code>server 1.debain.pool.ntp.org iburst</code></p>
<p><code>server 2.debain.pool.ntp.org iburst</code></p>
<p><code>server 3.debain.pool.ntp.org iburst</code></p>
<p>调整服务器为<code>server asia.pool.ntp.org iburst</code>,我自己试了并不可以,于是我参考了一下我的Mac,用了苹果的时间同步服务器,最终修改为<code>server time.apple.com iburst</code>然后重启ntp服务<br><code>sudo service ntp restart</code>即可。</p>
]]></content>
<summary type="html">
<p>今天刚到手树莓派,看了下时间,于是上网搜了下时间同步的问题。<br>然而我按照网上修改的同步服务器并不能解决问题,于是自己试着改了一下。</p>
<p>前提:你要选择正确了时区,运行<code>sudo raspi-config</code> 选择第4项,回车,继续选择第2
</summary>
<category term="树莓派" scheme="http://www.zhishuo.info/categories/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
<category term="树莓派" scheme="http://www.zhishuo.info/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
<category term="ntp同步" scheme="http://www.zhishuo.info/tags/ntp%E5%90%8C%E6%AD%A5/"/>
</entry>
<entry>
<title>读spring源码深度解析</title>
<link href="http://www.zhishuo.info/posts/java/%E8%AF%BBspring%E6%BA%90%E7%A0%81%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90.html"/>
<id>http://www.zhishuo.info/posts/java/读spring源码深度解析.html</id>
<published>2015-11-12T03:35:44.000Z</published>
<updated>2016-11-03T08:10:39.000Z</updated>
<content type="html"><![CDATA[<p>工作也有几年了,以前也尝试过想要读spring源码,但由于前几年没有参考资料,所以很难去理解,最近发现一本<code>spring源码深度解析</code>,怀着试试看的心情,踏上了源码之路。</p>
<p>记录一下自己遇到的问题,初次读完之后感觉到了spring的强大之处,处处都是设计模式,把代码拆的很分散,但理解上就很难了。</p>
<ol>
<li>源码下载,spring最新的git源码地址为<a href="https://github.com/spring-projects/spring-framework.git" target="_blank" rel="external">spring源码</a></li>
<li>由于spring3.X以后就是使用gradle构建了,所以还需要安装gradle,Mac OS 可以使用HomeBrew brew install gradle 即可</li>
<li>安装好gradle以后,可以在想要查看的spring项目下执行gradle eclipse 生成eclipse的配置文件</li>
<li>最后在Eclipse->import->existing Eclipse projects进行导入就可以了</li>
<li>书中有一个查看类实现的接口图,一开始不知道在哪使用,后来发现对着想要查看的类右击,出现选项菜单,点击->Open Type Hierarchy 即可,默认是显示的子类实现,然后点击上方小图标show the supertype Hierarchy即可</li>
<li>书中作者看到了哪个类中没有做明确提示,如果找不到需要自行搜索,我用的<a href="https://spring.io/tools" target="_blank" rel="external">STS</a>,commond+shift+l 可按关键字搜索</li>
</ol>
]]></content>
<summary type="html">
<p>工作也有几年了,以前也尝试过想要读spring源码,但由于前几年没有参考资料,所以很难去理解,最近发现一本<code>spring源码深度解析</code>,怀着试试看的心情,踏上了源码之路。</p>
<p>记录一下自己遇到的问题,初次读完之后感觉到了spring的强大之处
</summary>
<category term="java" scheme="http://www.zhishuo.info/categories/java/"/>
<category term="spring" scheme="http://www.zhishuo.info/tags/spring/"/>
<category term="spring源码" scheme="http://www.zhishuo.info/tags/spring%E6%BA%90%E7%A0%81/"/>
</entry>
<entry>
<title>hexo博客提交到搜索引擎</title>
<link href="http://www.zhishuo.info/posts/hexo/hexo%E5%8D%9A%E5%AE%A2%E6%8F%90%E4%BA%A4%E5%88%B0%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E.html"/>
<id>http://www.zhishuo.info/posts/hexo/hexo博客提交到搜索引擎.html</id>
<published>2014-06-22T02:28:55.000Z</published>
<updated>2016-11-15T16:16:47.000Z</updated>
<content type="html"><![CDATA[<p>使用了hexo搭建博客之后,于是想把博客地址提交到搜索引擎当中,让搜索引擎来收录,在提交搜索引擎的时候遇到了一些问题记录下来。<br>让搜索引擎收录的方式有以下几种:</p>
<ol>
<li>提交某个html文件到你的博客根目录下</li>
<li>添加网站的meta标签</li>
<li>CNAME方式</li>
</ol>
<p>当我使用第1种方式时,发现其实上传的文件是在index文件中包含了进来,所以搜索引擎不会识别,所以使用了第2种方式。<br><a id="more"></a><br>在使用第2种方式时,是需要给index.html添加meta标签,使用hexo的话,如果直接修改.deploy是不可以的,因为执行hexo clean,hexo g两条命令后,是会把文件覆盖的。所以我直接修改了主题中的模板文件,这样在生成index.html的时候就会自动生成进来了。</p>
<p>修改<code>themes</code>-<code>tyrant</code>-<code>layout</code>-<code>_partial</code>-<code>head.ejs</code>文件把meta内容直接加入head即可。</p>
<p>ps:<code>tyrant</code>为博主使用的主题,修改时只需要修改自己主题下的此文件即可。</p>
]]></content>
<summary type="html">
<p>使用了hexo搭建博客之后,于是想把博客地址提交到搜索引擎当中,让搜索引擎来收录,在提交搜索引擎的时候遇到了一些问题记录下来。<br>让搜索引擎收录的方式有以下几种:</p>
<ol>
<li>提交某个html文件到你的博客根目录下</li>
<li>添加网站的meta标签</li>
<li>CNAME方式</li>
</ol>
<p>当我使用第1种方式时,发现其实上传的文件是在index文件中包含了进来,所以搜索引擎不会识别,所以使用了第2种方式。<br>
</summary>
<category term="hexo" scheme="http://www.zhishuo.info/categories/hexo/"/>
<category term="搜索引擎" scheme="http://www.zhishuo.info/tags/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/"/>
</entry>
<entry>
<title>hexo 搭建 github pages 问题</title>
<link href="http://www.zhishuo.info/posts/hexo/hexo-%E6%90%AD%E5%BB%BA-github-pages--%E9%97%AE%E9%A2%98.html"/>
<id>http://www.zhishuo.info/posts/hexo/hexo-搭建-github-pages--问题.html</id>
<published>2014-05-31T10:45:03.000Z</published>
<updated>2016-11-15T16:16:07.000Z</updated>
<content type="html"><![CDATA[<p>由于自己想搭建一个免费的博客,所以找到了万能的github,但在搭建的过程中遇到了几个小问题,所以记下来<br>基本搭建的方法都是参考于<a href="http://ibruce.info/2013/11/22/hexo-your-blog/" target="_blank" rel="external">利用github搭建博客</a><br>这个博主已经写的很全了,搭建的过程就不一一提了说下遇到的问题</p>
<ol>
<li>hexo上传后博客还是官方的<br>建立了github pages的项目,并且使用了官方的建立建好了一个博客,官方默认会在master分支上开出一个gh-pages分支,然后查了官网资料说所有的内容都是显示gh-pages分支上的,但是我用hexo却传不到gh-pages分支上,并且传到了master分支内容也不会显示,后来找到原因是因为我建立的项目名字不是以#你的github帐户.github.io#命名的,所以会使hexo的上传程序传到master分支上也不会起作用,把项目名修改之后就可以了。<a id="more"></a></li>
<li>添加多说的问题<br>在官网注册后,复制了多说代码,按<a href="http://www.leejianyang.com/2014/05/25/duoshuo_tutorial/" target="_blank" rel="external">教程</a>做完之后发现多说模块不显示,所以查看了comment.ejs源代码<pre><code><% if (config.disqus_shortname && page.comments){ %>
</code></pre>这里是2个判断,把config.disqus_shortname && 删掉之后,就可以正常使用了。</li>
</ol>
]]></content>
<summary type="html">
<p>由于自己想搭建一个免费的博客,所以找到了万能的github,但在搭建的过程中遇到了几个小问题,所以记下来<br>基本搭建的方法都是参考于<a href="http://ibruce.info/2013/11/22/hexo-your-blog/">利用github搭建博客</a><br>这个博主已经写的很全了,搭建的过程就不一一提了说下遇到的问题</p>
<ol>
<li>hexo上传后博客还是官方的<br>建立了github pages的项目,并且使用了官方的建立建好了一个博客,官方默认会在master分支上开出一个gh-pages分支,然后查了官网资料说所有的内容都是显示gh-pages分支上的,但是我用hexo却传不到gh-pages分支上,并且传到了master分支内容也不会显示,后来找到原因是因为我建立的项目名字不是以#你的github帐户.github.io#命名的,所以会使hexo的上传程序传到master分支上也不会起作用,把项目名修改之后就可以了。
</summary>
<category term="hexo" scheme="http://www.zhishuo.info/categories/hexo/"/>
<category term="hexo" scheme="http://www.zhishuo.info/tags/hexo/"/>
<category term="github pages" scheme="http://www.zhishuo.info/tags/github-pages/"/>
</entry>
<entry>
<title>我为什么开始写博客</title>
<link href="http://www.zhishuo.info/posts/uncategorized/%E6%88%91%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BC%80%E5%A7%8B%E5%86%99%E5%8D%9A%E5%AE%A2.html"/>
<id>http://www.zhishuo.info/posts/uncategorized/我为什么开始写博客.html</id>
<published>2014-05-31T10:14:02.000Z</published>
<updated>2016-11-03T08:10:39.000Z</updated>
<content type="html"><![CDATA[<p>最近参加了几场印象笔记的活动,听大家分享了一些自己坚持做的事,由于最近自己也在整理自己琐碎的事情,感觉虽然自己在技术上一直在积累但那都是自己的眼光和认识,如果与大家分享出来之后这样可以帮助自己有很大的提升。<br>最近这一年来,在新的公司里自己有了很大的成长,但是发现这些成长记录其实只记录在我的印象笔记里或者是自己的经验上,并没有真正的分享出来,以后在这里记录自己遇到的问题以及一些正在学的技术,自己的理解和学习记录。<br>希望在这里可以记录下自己技术成长的轨迹,以便和大家共同成长。</p>
]]></content>
<summary type="html">
<p>最近参加了几场印象笔记的活动,听大家分享了一些自己坚持做的事,由于最近自己也在整理自己琐碎的事情,感觉虽然自己在技术上一直在积累但那都是自己的眼光和认识,如果与大家分享出来之后这样可以帮助自己有很大的提升。<br>最近这一年来,在新的公司里自己有了很大的成长,但是发现这些成
</summary>
<category term="博客" scheme="http://www.zhishuo.info/tags/%E5%8D%9A%E5%AE%A2/"/>