-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
1829 lines (878 loc) · 880 KB
/
search.xml
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"?>
<search>
<entry>
<title>go发送http请求</title>
<link href="/2024/07/21/go%E5%8F%91%E9%80%81http%E8%AF%B7%E6%B1%82/"/>
<url>/2024/07/21/go%E5%8F%91%E9%80%81http%E8%AF%B7%E6%B1%82/</url>
<content type="html"><![CDATA[<h1 id="go发送http请求"><a href="#go发送http请求" class="headerlink" title="go发送http请求"></a>go发送http请求</h1><h2 id="Get"><a href="#Get" class="headerlink" title="Get"></a>Get</h2><p>发送get请求</p><h3 id="不带参数"><a href="#不带参数" class="headerlink" title="不带参数"></a>不带参数</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 转发地址</span></span><br><span class="line">Url := <span class="string">"在此填入你的url"</span></span><br><span class="line">resp, err := http.Get(url)</span><br><span class="line"><span class="comment">// 解析响应数据</span></span><br><span class="line"><span class="keyword">defer</span> resp.Body.Close()</span><br><span class="line">body, err := ioutil.ReadAll(resp.Body)</span><br><span class="line"><span class="keyword">var</span> resMap <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}</span><br><span class="line">err = json.Unmarshal(body, &resMap)</span><br></pre></td></tr></table></figure><h3 id="带参数"><a href="#带参数" class="headerlink" title="带参数"></a>带参数</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 转发地址</span></span><br><span class="line">Url := <span class="string">"在此填入你的url"</span></span><br><span class="line">req, err := http.NewRequest(<span class="string">"GET"</span>, url, <span class="literal">nil</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> log.Println(err)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">req.AddCookie(cookie)</span><br><span class="line">resp, err := client.Do(req)</span><br><span class="line"><span class="comment">// 解析响应数据</span></span><br><span class="line"><span class="keyword">defer</span> resp.Body.Close()</span><br><span class="line">body, err := ioutil.ReadAll(resp.Body)</span><br><span class="line"><span class="keyword">var</span> resMap <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}</span><br><span class="line">err = json.Unmarshal(body, &resMap)</span><br></pre></td></tr></table></figure><h2 id="Post"><a href="#Post" class="headerlink" title="Post"></a>Post</h2><p>发送post请求</p><h3 id="转发文件"><a href="#转发文件" class="headerlink" title="转发文件"></a>转发文件</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取file文件,进行转发</span></span><br><span class="line">file, header, err := c.Request.FormFile(<span class="string">"file"</span>)</span><br><span class="line"><span class="keyword">defer</span> file.Close()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 转发地址</span></span><br><span class="line">Url := <span class="string">"在此填入你的url"</span></span><br><span class="line"><span class="comment">// 创建缓冲区</span></span><br><span class="line">buffers := &bytes.Buffer{}</span><br><span class="line"><span class="comment">// 创建输入流</span></span><br><span class="line">writer := multipart.NewWriter(buffers)</span><br><span class="line"><span class="comment">// 创建请求体中form-data的file参数</span></span><br><span class="line">formFile, err := writer.CreateFormFile(<span class="string">"file"</span>, header.Filename)</span><br><span class="line"><span class="comment">// 拷贝文件</span></span><br><span class="line">io.Copy(formFile, file)</span><br><span class="line"><span class="comment">// 关闭输入流</span></span><br><span class="line">writer.Close()</span><br><span class="line"><span class="comment">// 创建post请求</span></span><br><span class="line">req, err := http.NewRequest(<span class="string">"POST"</span>, Url, buffers)</span><br><span class="line"><span class="comment">// 设置请求头</span></span><br><span class="line">req.Header.Set(<span class="string">"Content-Type"</span>, writer.FormDataContentType())</span><br><span class="line"><span class="comment">// 创建http客户端</span></span><br><span class="line">client := &http.Client{}</span><br><span class="line"><span class="comment">// 如果是https请求</span></span><br><span class="line"><span class="comment">//client = &http.Client{</span></span><br><span class="line"><span class="comment">// // 创建一个自定义的 Transport,并禁用证书验证</span></span><br><span class="line"><span class="comment">// Transport: &http.Transport{</span></span><br><span class="line"><span class="comment">// TLSClientConfig: &tls.Config{InsecureSkipVerify: true},</span></span><br><span class="line"><span class="comment">// },</span></span><br><span class="line"><span class="comment">//}</span></span><br><span class="line"><span class="comment">// 执行http请求</span></span><br><span class="line">resp, err := client.Do(req)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"转发文件失败出错error............"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 解析响应数据</span></span><br><span class="line"><span class="keyword">defer</span> resp.Body.Close()</span><br><span class="line">body, err := ioutil.ReadAll(resp.Body)</span><br><span class="line"><span class="keyword">var</span> resMap <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}</span><br><span class="line">err = json.Unmarshal(body, &resMap)</span><br></pre></td></tr></table></figure><h3 id="转发请求"><a href="#转发请求" class="headerlink" title="转发请求"></a>转发请求</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 转发地址</span></span><br><span class="line">Url := <span class="string">"在此填入你的url"</span></span><br><span class="line"><span class="comment">// 创建请求体中的json参数</span></span><br><span class="line">sendData := <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}{</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="string">"list"</span>: []<span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}{</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"id"</span>: <span class="number">10001</span>,</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"testing"</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"taskId"</span>: <span class="number">10001</span>,</span><br><span class="line"> <span class="string">"subjectId"</span>: <span class="number">10001</span>,</span><br><span class="line"> <span class="string">"status"</span>: <span class="literal">true</span>,</span><br><span class="line">}</span><br><span class="line"><span class="comment">// map转json</span></span><br><span class="line">marshal, err := json.Marshal(sendData)</span><br><span class="line"><span class="comment">// fmt.Println(string(marshal))</span></span><br><span class="line"><span class="comment">// 创建缓冲区</span></span><br><span class="line">buffer := bytes.NewBuffer(marshal)</span><br><span class="line"><span class="comment">// 创建post请求</span></span><br><span class="line">req, err := http.NewRequest(<span class="string">"POST"</span>, Url, buffer)</span><br><span class="line"><span class="comment">// 设置请求头</span></span><br><span class="line">req.Header.Set(<span class="string">"Content-Type"</span>, <span class="string">"application/json"</span>)</span><br><span class="line"><span class="comment">// 创建http客户端</span></span><br><span class="line">client := &http.Client{}</span><br><span class="line"><span class="comment">// 如果是https请求</span></span><br><span class="line"><span class="comment">//client = &http.Client{</span></span><br><span class="line"><span class="comment">// // 创建一个自定义的 Transport,并禁用证书验证</span></span><br><span class="line"><span class="comment">// Transport: &http.Transport{</span></span><br><span class="line"><span class="comment">// TLSClientConfig: &tls.Config{InsecureSkipVerify: true},</span></span><br><span class="line"><span class="comment">// },</span></span><br><span class="line"><span class="comment">//}</span></span><br><span class="line"><span class="comment">// 执行http请求</span></span><br><span class="line">resp, err = client.Do(req)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"转发请求出错error............"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 解析响应数据</span></span><br><span class="line"><span class="keyword">defer</span> resp.Body.Close()</span><br><span class="line">body, err := ioutil.ReadAll(resp.Body)</span><br><span class="line"><span class="keyword">var</span> resMap <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}</span><br><span class="line">err = json.Unmarshal(body, &resMap)</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> go </category>
</categories>
<tags>
<tag> go </tag>
<tag> http </tag>
</tags>
</entry>
<entry>
<title>go生成随机字符串</title>
<link href="/2024/03/01/go%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/"/>
<url>/2024/03/01/go%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/</url>
<content type="html"><![CDATA[<h1 id="生成随机字符串"><a href="#生成随机字符串" class="headerlink" title="生成随机字符串"></a>生成随机字符串</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> RandomUtil</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"fmt"</span></span><br><span class="line"><span class="string">"math/rand"</span></span><br><span class="line"><span class="string">"time"</span></span><br><span class="line"><span class="string">"unsafe"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 随机数种子源</span></span><br><span class="line"><span class="keyword">var</span> random = rand.NewSource(time.Now().UnixNano())</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"><span class="comment">// 定义可用字符的字符串</span></span><br><span class="line">letters = <span class="string">"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"</span></span><br><span class="line"><span class="comment">// 62个字符能用6个比特位数表示完</span></span><br><span class="line">letterIdBits = <span class="number">6</span></span><br><span class="line"><span class="comment">// 最大的字母id掩码</span></span><br><span class="line">letterIdMask = <span class="number">1</span><<letterIdBits - <span class="number">1</span></span><br><span class="line"><span class="comment">// 可用次数的最大值</span></span><br><span class="line">letterIdMax = letterIdMask / letterIdBits</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// BuildRandomString 生成指定长度的随机字符串</span></span><br><span class="line"><span class="comment">// length 字符串生成长度</span></span><br><span class="line"><span class="comment">// 生成指定长度的随机字符串</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">BuildRandomString</span><span class="params">(length <span class="keyword">int</span>)</span> <span class="title">string</span></span> {</span><br><span class="line"><span class="comment">// 创建一个长度为 length 的字节切片</span></span><br><span class="line">bytes := <span class="built_in">make</span>([]<span class="keyword">byte</span>, length)</span><br><span class="line"><span class="comment">// 循环生成随机字符串</span></span><br><span class="line"><span class="keyword">for</span> i, cache, remain := length<span class="number">-1</span>, random.Int63(), letterIdMax; i >= <span class="number">0</span>; {</span><br><span class="line"><span class="comment">// 检查随机数生成器是否用尽所有随机数</span></span><br><span class="line"><span class="keyword">if</span> remain == <span class="number">0</span> {</span><br><span class="line">cache, remain = random.Int63(), letterIdMax</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 从可用字符的字符串中随机选择一个字符</span></span><br><span class="line"><span class="keyword">if</span> idx := <span class="keyword">int</span>(cache & letterIdMask); idx < <span class="built_in">len</span>(letters) {</span><br><span class="line">bytes[i] = letters[idx]</span><br><span class="line">i--</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 右移比特位数,为下次选择字符做准备</span></span><br><span class="line">cache >>= letterIdBits</span><br><span class="line">remain--</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 将字节切片转换为字符串并返回</span></span><br><span class="line"><span class="keyword">return</span> *(*<span class="keyword">string</span>)(unsafe.Pointer(&bytes))</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> go </category>
</categories>
<tags>
<tag> go </tag>
<tag> random </tag>
</tags>
</entry>
<entry>
<title>linux常用命令</title>
<link href="/2024/02/01/linux%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/"/>
<url>/2024/02/01/linux%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</url>
<content type="html"><![CDATA[<h1 id="linux常用命令"><a href="#linux常用命令" class="headerlink" title="linux常用命令"></a>linux常用命令</h1><h2 id="lsof"><a href="#lsof" class="headerlink" title="lsof"></a>lsof</h2><p>lsof 是 List Open File 的缩写, 它主要用来获取被进程打开文件的信息。</p><p>使用 <code>lsof -i:端口号</code> 可以获得所有在指定端口号上打开的文件。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# lsof -i:3306</span><br><span class="line">COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME</span><br><span class="line">ossec-dbd 48382 ossecm 3u IPv4 5428466 0t0 TCP localhost:54004->localhost:mysql (ESTABLISHED)</span><br><span class="line">mysqld 94358 mysql 4u IPv6 5423584 0t0 TCP localhost:mysql->localhost:54004 (ESTABLISHED)</span><br><span class="line">mysqld 94358 mysql 26u IPv6 4226501 0t0 TCP *:mysql (LISTEN)</span><br></pre></td></tr></table></figure><h2 id="netstat"><a href="#netstat" class="headerlink" title="netstat"></a>netstat</h2><p>netstat 命令用于显示系统的网络状态</p><p>使用<code>netstat -anp</code>显示系统端口使用情况</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# netstat -anp|grep 3306</span><br><span class="line">tcp 0 0 127.0.0.1:54004 127.0.0.1:3306 ESTABLISHED 48382/ossec-dbd</span><br><span class="line">tcp6 0 0 :::3306 :::* LISTEN 94358/mysqld</span><br><span class="line">tcp6 0 0 127.0.0.1:3306 127.0.0.1:54004 ESTABLISHED 94358/mysqld</span><br></pre></td></tr></table></figure><h2 id="tar"><a href="#tar" class="headerlink" title="tar"></a>tar</h2><p>tar用于打包和解压文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 打包文件</span></span><br><span class="line"><span class="meta">#</span><span class="bash">压缩包名称 压缩目录</span></span><br><span class="line">tar -czvf mydata.tar.gz /data/</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 解压文件</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 解压包 指定解压目录</span></span><br><span class="line">tar -xzvf myetc.tar.gz -C /data/</span><br></pre></td></tr></table></figure><h2 id="telnet"><a href="#telnet" class="headerlink" title="telnet"></a>telnet</h2><p>telnet用于远程连接</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 连接192.168.56.135的8888端口</span></span><br><span class="line">[root@localhost ~]# telnet 192.168.56.135 8888</span><br><span class="line">Trying 192.168.56.135...</span><br><span class="line">Connected to 192.168.56.135.</span><br></pre></td></tr></table></figure><h2 id="nc"><a href="#nc" class="headerlink" title="nc"></a>nc</h2><p>nc是netcat的简写,nc是款多功能的CLI工具</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 监听8888端口</span></span><br><span class="line">[root@localhost ~]# nc -l 8888 -v</span><br><span class="line">Ncat: Version 7.50 ( https://nmap.org/ncat )</span><br><span class="line">Ncat: Listening on :::8888</span><br><span class="line">Ncat: Listening on 0.0.0.0:8888</span><br><span class="line">Ncat: Connection from 192.168.56.136.</span><br><span class="line">Ncat: Connection from 192.168.56.136:53218.</span><br></pre></td></tr></table></figure><h2 id="find"><a href="#find" class="headerlink" title="find"></a>find</h2><p>find用于在指定目录及其子目录中搜索文件或目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 在/var/ossec目录下查找.txt结尾的文件</span></span><br><span class="line">[root@localhost ossec]# find /var/ossec/ -name "*.txt"</span><br><span class="line">/var/ossec/etc/shared/cis_apache2224_rcl.txt</span><br><span class="line">/var/ossec/etc/shared/cis_debianlinux7-8_L2_rcl.txt</span><br><span class="line">/var/ossec/etc/shared/cis_mysql5-6_community_rcl.txt</span><br><span class="line">/var/ossec/etc/shared/cis_rhel5_linux_rcl.txt</span><br><span class="line">/var/ossec/etc/shared/cis_rhel7_linux_rcl.txt</span><br><span class="line">/var/ossec/etc/shared/cis_sles11_linux_rcl.txt</span><br><span class="line">......</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> linux </tag>
</tags>
</entry>
<entry>
<title>服务器设置静态ip</title>
<link href="/2024/01/15/%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AE%BE%E7%BD%AE%E9%9D%99%E6%80%81ip/"/>
<url>/2024/01/15/%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AE%BE%E7%BD%AE%E9%9D%99%E6%80%81ip/</url>
<content type="html"><![CDATA[<h1 id="设置静态ip"><a href="#设置静态ip" class="headerlink" title="设置静态ip"></a>设置静态ip</h1><h2 id="CentOS7"><a href="#CentOS7" class="headerlink" title="CentOS7"></a>CentOS7</h2><p>修改网卡</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 修改指定的网卡,如eth0、ens33</span></span><br><span class="line">vim /etc/sysconfig/network-scripts/ifcfg-ens33</span><br></pre></td></tr></table></figure><p>修改内容</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 修改</span></span><br><span class="line">BOOTPROTO=dhcp --> BOOTPROTO=static</span><br><span class="line">......</span><br><span class="line">ONBOOT=no --> ONBOOT=yes</span><br><span class="line"><span class="meta">#</span><span class="bash"> 新增</span></span><br><span class="line">IPADDR=192.168.1.100 # 你要设置的固定 IP 地址</span><br><span class="line">NETMASK=255.255.252.0 # 子网掩码</span><br><span class="line">GATEWAY=192.168.0.1 # 网关 IP 地址</span><br><span class="line">DNS1=8.8.8.8 # 首选 DNS 服务器</span><br><span class="line">DNS2=114.114.114.114 # 备选 DNS 服务器</span><br></pre></td></tr></table></figure><p>重启网卡</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart network</span><br></pre></td></tr></table></figure><p>查看修改后网卡信息</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip a show ifcfg-ens33</span><br></pre></td></tr></table></figure><h2 id="Ubuntu20-04"><a href="#Ubuntu20-04" class="headerlink" title="Ubuntu20.04"></a>Ubuntu20.04</h2><p>修改网卡</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/netplan/00-installer-config.yaml</span><br></pre></td></tr></table></figure><p>修改内容</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">network:</span><br><span class="line"> version: 2</span><br><span class="line"> renderer: NetworkManager</span><br><span class="line"> ethernets:</span><br><span class="line"> ens3: # 网卡名称</span><br><span class="line"> dhcp4: no # 关闭dhcp</span><br><span class="line"> addresses: [192.168.1.100/22] # 静态ip</span><br><span class="line"> gateway4: 192.168.0.1 # 网关ip地址</span><br><span class="line"> nameservers: # DNS服务器</span><br><span class="line"> addresses: [8.8.8.8, 114.114.114.114] # DNS服务器地址</span><br></pre></td></tr></table></figure><p>重启网卡</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo netplan apply</span><br></pre></td></tr></table></figure><p>查看修改后网卡信息</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip a show dev ens3</span><br></pre></td></tr></table></figure><h2 id="Debian11"><a href="#Debian11" class="headerlink" title="Debian11"></a>Debian11</h2><p>修改网卡</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/network/interfaces</span><br></pre></td></tr></table></figure><p>修改内容</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 自动启用网卡</span></span><br><span class="line">allow-hotplug ens18</span><br><span class="line">auto ens18</span><br><span class="line"><span class="meta">#</span><span class="bash"> 将dhcp改成static</span></span><br><span class="line">iface ens18 inet static</span><br><span class="line">address 192.168.1.100# 地址</span><br><span class="line">netmask 255.255.252.0# 子网掩码</span><br><span class="line">gateway 192.168.0.1 # 网关</span><br></pre></td></tr></table></figure><p>重启网卡</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service networking restart</span><br></pre></td></tr></table></figure><p>查看修改后网卡信息</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip a show ens18</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 基础 </category>
</categories>
<tags>
<tag> linux </tag>
</tags>
</entry>
<entry>
<title>go基准测试</title>
<link href="/2023/11/30/go%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95/"/>
<url>/2023/11/30/go%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95/</url>
<content type="html"><![CDATA[<h1 id="go基准测试"><a href="#go基准测试" class="headerlink" title="go基准测试"></a>go基准测试</h1><p>测试时间为5秒钟</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">zhb@DESKTOP-<span class="number">80</span>JJESV MINGW64 /d/Go/WorkSpace/mytest/com/zhb/utils</span><br><span class="line">$ go test -benchmem -benchtime=<span class="number">5</span>s -bench .</span><br><span class="line"><span class="function">goos: <span class="title">windows</span></span></span><br><span class="line"><span class="function"><span class="title">goarch</span>: <span class="title">amd64</span></span></span><br><span class="line"><span class="function"><span class="title">pkg</span>: <span class="title">mytest</span>/<span class="title">com</span>/<span class="title">zhb</span>/<span class="title">utils</span></span></span><br><span class="line"><span class="function"><span class="title">cpu</span>: <span class="title">Intel</span>(<span class="title">R</span>) <span class="title">Core</span>(<span class="title">TM</span>) <span class="title">i5</span>-10400 <span class="title">CPU</span> @ 2.90<span class="title">GHz</span></span></span><br><span class="line"><span class="function"><span class="title">BenchmarkApproach1</span>-12 74540550 78.80 <span class="title">ns</span>/<span class="title">op</span> 32 <span class="title">B</span>/<span class="title">op</span> 1 <span class="title">allocs</span>/<span class="title">op</span></span></span><br><span class="line"><span class="function"><span class="title">BenchmarkApproach4</span>-12 658540 9213 <span class="title">ns</span>/<span class="title">op</span> 5488 <span class="title">B</span>/<span class="title">op</span> 4 <span class="title">allocs</span>/<span class="title">op</span></span></span><br><span class="line"><span class="function"><span class="title">BenchmarkApproach5</span>-12 33707202 177.1 <span class="title">ns</span>/<span class="title">op</span> 16 <span class="title">B</span>/<span class="title">op</span> 1 <span class="title">allocs</span>/<span class="title">op</span></span></span><br><span class="line"><span class="function"><span class="title">PASS</span></span></span><br><span class="line"><span class="function"><span class="title">ok</span> <span class="title">mytest</span>/<span class="title">com</span>/<span class="title">zhb</span>/<span class="title">utils</span> 18.324<span class="title">s</span></span></span><br></pre></td></tr></table></figure><p>测试次数为10000次</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">zhb@DESKTOP<span class="number">-80</span>JJESV MINGW64 /d/Go/WorkSpace/mytest/com/zhb/utils</span><br><span class="line">$ <span class="keyword">go</span> test -benchmem -benchtime=<span class="number">10000</span>x -bench .</span><br><span class="line">goos: windows</span><br><span class="line">goarch: amd64</span><br><span class="line">pkg: mytest/com/zhb/utils</span><br><span class="line">cpu: Intel(R) Core(TM) i5<span class="number">-10400</span> CPU @ <span class="number">2.90</span>GHz</span><br><span class="line">BenchmarkApproach1<span class="number">-12</span> <span class="number">10000</span> <span class="number">110.8</span> ns/op <span class="number">32</span> B/op <span class="number">1</span> allocs/op</span><br><span class="line">BenchmarkApproach4<span class="number">-12</span> <span class="number">10000</span> <span class="number">9462</span> ns/op <span class="number">5488</span> B/op <span class="number">4</span> allocs/op</span><br><span class="line">BenchmarkApproach5<span class="number">-12</span> <span class="number">10000</span> <span class="number">100.0</span> ns/op <span class="number">16</span> B/op <span class="number">1</span> allocs/op</span><br><span class="line">PASS</span><br><span class="line">ok mytest/com/zhb/utils <span class="number">0.147</span>s</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> go </category>
</categories>
<tags>
<tag> go </tag>
<tag> testing </tag>
</tags>
</entry>
<entry>
<title>docker部署jenkins自动打包go</title>
<link href="/2023/11/20/docker%E9%83%A8%E7%BD%B2jenkins%E8%87%AA%E5%8A%A8%E6%89%93%E5%8C%85go/"/>
<url>/2023/11/20/docker%E9%83%A8%E7%BD%B2jenkins%E8%87%AA%E5%8A%A8%E6%89%93%E5%8C%85go/</url>
<content type="html"><![CDATA[<h1 id="docker部署jenkins自动打包go"><a href="#docker部署jenkins自动打包go" class="headerlink" title="docker部署jenkins自动打包go"></a>docker部署jenkins自动打包go</h1><p>参考文章:</p><ol><li><a href="https://blog.csdn.net/qq_31635851/article/details/109623908">【Jenkins】如何升级Jenkins版本_jenkins升级-CSDN博客</a></li><li><a href="https://blog.csdn.net/larson_test/article/details/107973715">Jenkins安装插件失败问题解决方案(必定有效)_this version of the plugin exists but it is not be-CSDN博客</a></li><li><a href="https://developer.aliyun.com/article/892646">配置码云代码仓库实现自动部署</a></li></ol><h2 id="安装jenkins"><a href="#安装jenkins" class="headerlink" title="安装jenkins"></a>安装jenkins</h2><p>默认已完成<code>docker</code>和<code>docker-compose</code><br>安装,如果没有请参考下文<a href="https://www.zhbblog.top/2023/09/10/%E4%BD%BF%E7%94%A8docker%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE/">使用docker搭建项目 | 柳门竹巷 (zhbblog.top)</a></p><ol><li><p>检查<code>docker</code>和<code>docker-compose</code>安装</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 查询docker和docker-compose版本</span></span><br><span class="line">docker -v</span><br><span class="line">docker-compose -v</span><br></pre></td></tr></table></figure></li><li><p>拉取<code>jenkins</code>镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 拉取jenkins镜像</span></span><br><span class="line">docker pull jenkins/jenkins</span><br></pre></td></tr></table></figure></li><li><p>配置docker-compose.yml</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 配置docker-compose.yml</span></span><br><span class="line"><span class="attr">version:</span> <span class="string">"3"</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line"> <span class="attr">jenkins:</span></span><br><span class="line"> <span class="attr">image:</span> <span class="string">jenkins/jenkins:latest</span></span><br><span class="line"> <span class="attr">container_name:</span> <span class="string">jenkins</span></span><br><span class="line"> <span class="attr">ports:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="number">8070</span><span class="string">:8080</span></span><br><span class="line"> <span class="bullet">-</span> <span class="number">50000</span><span class="string">:50000</span></span><br><span class="line"> <span class="attr">volumes:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">/data/jenkins/:/var/jenkins_home</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">/var/run/docker.sock:/var/run/docker.sock</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">/usr/bin/docker:/usr/bin/docker</span></span><br><span class="line"> <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line"> <span class="attr">user:</span> <span class="string">root</span></span><br></pre></td></tr></table></figure></li><li><p>运行容器</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 启动容器</span></span><br><span class="line">docker-compose up -d jenkins</span><br></pre></td></tr></table></figure></li><li><p>获取<code>jenkins</code>最新包</p><p>获取最新版的<code>jenkins</code>的<code>war</code><br>包,下载链接:<a href="https://mirrors.tuna.tsinghua.edu.cn/jenkins/war-stable/">Index of /jenkins/war-stable/latest/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror</a></p></li><li><p>将<code>jenkins</code>最新包上传服务器,拷贝到容器中</p><p>建议先阅读:<a href="https://blog.csdn.net/qq_31635851/article/details/109623908">【Jenkins】如何升级Jenkins版本_jenkins升级-CSDN博客</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 将最新包拷贝到容器中</span></span><br><span class="line">docker cp jenkins.war jenkins:/usr/share/jenkins/</span><br></pre></td></tr></table></figure></li><li><p>重启容器</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 重启容器</span></span><br><span class="line">docker restart jenkins</span><br></pre></td></tr></table></figure></li><li><p>进入管理页面</p><p>在浏览器输入 <code>http://IP:8070</code> 进入 <code>jenkins</code> 管理页面</p></li><li><p>查看<code>jenkins</code>密码</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 进入docker容器</span></span><br><span class="line">docker exec -it jenkins bash</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看密码</span></span><br><span class="line">cat /var/jenkins_home/secrets/initialAdminPassword</span><br><span class="line"><span class="meta">#</span><span class="bash"> 初始密码</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 0bb8c530650643f6b486a26875bf2cee</span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 退出</span></span><br><span class="line">exit</span><br></pre></td></tr></table></figure></li><li><p>(可选)如果初始化插件下载失败</p><p>将下载源地址更换至:<a href="https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json%EF%BC%8C%E7%84%B6%E5%90%8E%E9%87%8D%E5%90%AF%E5%AE%B9%E5%99%A8%E5%8D%B3%E5%8F%AF%E3%80%82">https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json,然后重启容器即可。</a></p><p>如果仍不成功,请自行阅读<a href="https://blog.csdn.net/larson_test/article/details/107973715">Jenkins安装插件失败问题解决方案(必定有效)_this version of the plugin exists but it is not be-CSDN博客</a></p></li></ol><h2 id="编写Dockerfile"><a href="#编写Dockerfile" class="headerlink" title="编写Dockerfile"></a>编写Dockerfile</h2><p>请根据实际业务进行调整。</p><p><strong>本文为在<code>docker</code>容器中编译代码,并生成对应的<code>docker</code>镜像,并上传<code>docker-hub</code></strong></p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 拉取基础镜像</span></span><br><span class="line"><span class="keyword">FROM</span> golang:alpine</span><br><span class="line"><span class="comment"># 设置环境变量</span></span><br><span class="line"><span class="keyword">ENV</span> GO111MODULE=on \</span><br><span class="line"> CGO_ENABLED=<span class="number">0</span> \</span><br><span class="line"> GOOS=linux \</span><br><span class="line"> GOARCH=amd64 \</span><br><span class="line"> GOPROXY=<span class="string">"https://goproxy.cn,direct"</span></span><br><span class="line"><span class="comment"># 指定源码保存目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /home/sourceCode</span></span><br><span class="line"><span class="comment"># 复制源码</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . .</span></span><br><span class="line"><span class="comment"># 编译</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> go build -o main .</span></span><br><span class="line"><span class="comment"># 切换目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /go</span></span><br><span class="line"><span class="comment"># 拷贝文件(根据实际进行调整)</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> cp /home/sourceCode/main .</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> mkdir config</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> cp /home/sourceCode/config/config.yaml config/</span></span><br><span class="line"><span class="comment"># 设置执行权限</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> chmod +x main</span></span><br><span class="line"><span class="comment"># 清理源码文件</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> rm -rf /home/sourceCode</span></span><br><span class="line"><span class="comment"># 暴露端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8080</span></span><br><span class="line"><span class="comment"># 启动程序</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"/go/main"</span>]</span></span><br></pre></td></tr></table></figure><h2 id="配置jenkins项目"><a href="#配置jenkins项目" class="headerlink" title="配置jenkins项目"></a>配置jenkins项目</h2><p><strong>本文为在<code>docker</code>容器中编译代码,并生成对应的<code>docker</code>镜像,并上传<code>docker-hub</code></strong></p><p>请参考下文:<a href="https://developer.aliyun.com/article/892646">配置码云代码仓库实现自动部署</a>,只看最后部分</p><p>在构建的执行shell中填写对应的操作,<strong>请自行检查服务器中是否可以读取到对应配置,如果不能请自行替换其中的参数</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 登录docker-hub</span></span><br><span class="line">docker login -u ${harbor_user} -p ${harbor_password} ${harbor_addr}:${harbor_port}</span><br><span class="line"><span class="meta">#</span><span class="bash"> 构建镜像</span></span><br><span class="line">DOCKER_BUILDKIT=1 docker build -t ${harbor_addr}:${harbor_port}/(文件目录)/(镜像名):latest .</span><br><span class="line"><span class="meta">#</span><span class="bash"> 上传镜像</span></span><br><span class="line">docker push ${harbor_addr}:${harbor_port}/(文件目录)/(镜像名):latest</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> jenkins </category>
</categories>
<tags>
<tag> docker </tag>
<tag> go </tag>
<tag> jenkins </tag>
</tags>
</entry>
<entry>
<title>go操作minio</title>
<link href="/2023/11/10/go%E6%93%8D%E4%BD%9Cminio/"/>
<url>/2023/11/10/go%E6%93%8D%E4%BD%9Cminio/</url>
<content type="html"><![CDATA[<h1 id="go操作minio"><a href="#go操作minio" class="headerlink" title="go操作minio"></a>go操作minio</h1><p>参考文章:<a href="https://blog.csdn.net/weixin_40461281/article/details/118568289">docker-compose 搭建 minio 分布式对象存储 最新版(使用教程)_docker-compose minio-CSDN博客</a></p><h2 id="安装minio"><a href="#安装minio" class="headerlink" title="安装minio"></a>安装minio</h2><ol><li><p>安装docker</p><p>安装步骤详情请参考:<a href="https://www.zhbblog.top/2023/09/20/%E4%BD%BF%E7%94%A8docker%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE/#%E5%AE%89%E8%A3%85docker">使用docker搭建项目 | 柳门竹巷 (zhbblog.top)</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# docker -v</span><br><span class="line">Docker version 24.0.6, build ed223bc</span><br></pre></td></tr></table></figure></li><li><p>安装docker-compose</p><p>安装步骤详情请参考:<a href="https://www.zhbblog.top/2023/09/20/%E4%BD%BF%E7%94%A8docker%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE/#%E5%AE%89%E8%A3%85docker-compose">使用docker搭建项目 | 柳门竹巷 (zhbblog.top)</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# docker-compose -v</span><br><span class="line">Docker Compose version v2.20.3</span><br></pre></td></tr></table></figure></li><li><p>拉取minio镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull minio/minio</span><br></pre></td></tr></table></figure></li><li><p>创建minio对应文件夹</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 进入data目录</span></span><br><span class="line">cd /data</span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建minio目录</span></span><br><span class="line">mkdir minio</span><br><span class="line"><span class="meta">#</span><span class="bash"> 进入minio目录</span></span><br><span class="line">cd minio</span><br><span class="line"><span class="meta">#</span><span class="bash"> 在minio目录下创建data目录和config目录</span></span><br><span class="line">mkdir data</span><br><span class="line">mkdir config</span><br></pre></td></tr></table></figure></li><li><p>编写docker-compose.yml</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">'3'</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line"> <span class="attr">minio:</span></span><br><span class="line"> <span class="attr">image:</span> <span class="string">minio/minio</span></span><br><span class="line"> <span class="attr">hostname:</span> <span class="string">"minio"</span></span><br><span class="line"> <span class="attr">ports:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="number">9000</span><span class="string">:9000</span> <span class="comment"># api端口</span></span><br><span class="line"> <span class="bullet">-</span> <span class="number">9001</span><span class="string">:9001</span> <span class="comment"># 控制台端口</span></span><br><span class="line"> <span class="attr">environment:</span></span><br><span class="line"> <span class="attr">MINIO_ACCESS_KEY:</span> <span class="string">admin</span> <span class="comment">#管理后台用户名</span></span><br><span class="line"> <span class="attr">MINIO_SECRET_KEY:</span> <span class="string">admin123</span> <span class="comment">#管理后台密码,最小8个字符</span></span><br><span class="line"> <span class="attr">volumes:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">/data/minio/data:/data</span> <span class="comment">#映射当前目录下的data目录至容器内/data目录</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">/data/minio/config:/root/.minio/</span> <span class="comment">#映射配置目录</span></span><br><span class="line"> <span class="attr">command:</span> <span class="string">server</span> <span class="string">--console-address</span> <span class="string">':9001'</span> <span class="string">/data</span> <span class="comment">#指定容器中的目录 /data</span></span><br><span class="line"> <span class="attr">privileged:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">restart:</span> <span class="string">always</span></span><br></pre></td></tr></table></figure></li><li><p>在防火墙放行对应端口(如果防火墙开启的话)</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 添加防火墙放行规则</span></span><br><span class="line">firewall-cmd --zone=public --add-port=9000/tcp --permanent</span><br><span class="line">firewall-cmd --zone=public --add-port=9001/tcp --permanent</span><br><span class="line"><span class="meta">#</span><span class="bash"> 重新加载防火墙配置</span></span><br><span class="line">firewall-cmd --reload</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 重启防火墙(如果有必要的话)</span></span><br><span class="line">systemctl restart firewalld</span><br><span class="line"><span class="meta">#</span><span class="bash"> 或者</span></span><br><span class="line">service firewalld restart</span><br><span class="line"><span class="meta">#</span><span class="bash"> 如果重启防火墙,可能导致需要重启docker</span></span><br><span class="line">systemctl restart docker</span><br></pre></td></tr></table></figure></li><li><p>启动minio容器</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 请在docker-compose.yml所在目录下执行</span></span><br><span class="line">docker-compose up -d minio</span><br></pre></td></tr></table></figure></li><li><p>访问minio控制</p><p>浏览器访问:<code>服务器ip:9001</code>,例如<code>http://127.0.0.1:9001</code>,账号密码为<code>docker-compose.yml</code>中<code>MINIO_ACCESS_KEY</code>和<code>MINIO_SECRET_KEY</code>,例如本文中账号、密码为<code>admin</code>、<code>admin123</code>。</p></li></ol><h2 id="操作minio"><a href="#操作minio" class="headerlink" title="操作minio"></a>操作minio</h2><h3 id="编写minioUtil"><a href="#编写minioUtil" class="headerlink" title="编写minioUtil"></a>编写minioUtil</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> minioUtil</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"context"</span></span><br><span class="line"><span class="string">"fmt"</span></span><br><span class="line"><span class="string">"github.com/minio/minio-go/v7"</span></span><br><span class="line"><span class="string">"github.com/minio/minio-go/v7/pkg/credentials"</span></span><br><span class="line"><span class="string">"log"</span></span><br><span class="line"><span class="string">"mime/multipart"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line">endpoint <span class="keyword">string</span> = <span class="string">"127.0.0.1:9000"</span></span><br><span class="line">accessKeyID <span class="keyword">string</span> = <span class="string">"admin"</span></span><br><span class="line">secretAccessKey <span class="keyword">string</span> = <span class="string">"admin123"</span></span><br><span class="line">useSSL <span class="keyword">bool</span> = <span class="literal">false</span></span><br><span class="line">urlPrefix <span class="keyword">string</span> = <span class="string">"http://127.0.0.1:9000/"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">client *minio.Client</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</span><br><span class="line">c, err := minio.New(endpoint, &minio.Options{</span><br><span class="line">Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, <span class="string">""</span>),</span><br><span class="line">Secure: useSSL,</span><br><span class="line">})</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"><span class="built_in">panic</span>(fmt.Errorf(<span class="string">"failed to connect to minio: %s"</span>, err))</span><br><span class="line">}</span><br><span class="line">client = c</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查存储桶,如果不存在则创建存储桶</span></span><br><span class="line"><span class="comment">// @param bucketName: 存储桶名称</span></span><br><span class="line"><span class="comment">// @return bool, error 是否成功, 可能出现的错误</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">checkBucket</span><span class="params">(bucketName <span class="keyword">string</span>)</span> <span class="params">(<span class="keyword">bool</span>, error)</span></span> {</span><br><span class="line"><span class="comment">// 检查存储桶是否存在</span></span><br><span class="line"><span class="keyword">if</span> found, _ := client.BucketExists(context.Background(), bucketName); found {</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>, <span class="literal">nil</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 创建存储桶</span></span><br><span class="line"><span class="keyword">if</span> err := client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: <span class="string">"beijing"</span>}); err != <span class="literal">nil</span> {</span><br><span class="line">fmt.Println(<span class="string">"Error creating bucket:"</span>, err)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>, err</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>, <span class="literal">nil</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 构建文件名</span></span><br><span class="line"><span class="comment">// @param objectName: 存储桶名称</span></span><br><span class="line"><span class="comment">// @return string 生成后的文件名</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">buildFileName</span><span class="params">(objectName <span class="keyword">string</span>)</span> <span class="title">string</span></span> {</span><br><span class="line">index := strings.LastIndex(strings.TrimSpace(objectName), <span class="string">"."</span>)</span><br><span class="line">now := time.Now()</span><br><span class="line"><span class="keyword">return</span> fmt.Sprintf(<span class="string">"%s/%s_%d%s"</span>, now.Format(<span class="string">"2006/01/02"</span>), objectName[:index], now.UnixNano(), objectName[index:])</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// UploadFile 将给定的文件上传到指定的存储桶和对象名,并返回生成的URL。</span></span><br><span class="line"><span class="comment">// @param bucketName: 存储桶名称</span></span><br><span class="line"><span class="comment">// @param objectName: 对象名称</span></span><br><span class="line"><span class="comment">// @param fileHeader: 文件头信息</span></span><br><span class="line"><span class="comment">// @return string, error 返回上传成功的URL,可能出现的错误</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">UploadFile</span><span class="params">(bucketName <span class="keyword">string</span>, objectName <span class="keyword">string</span>, fileHeader *multipart.FileHeader)</span> <span class="params">(<span class="keyword">string</span>, error)</span></span> {</span><br><span class="line">file, err := fileHeader.Open()</span><br><span class="line"><span class="keyword">defer</span> file.Close()</span><br><span class="line"><span class="comment">// 检查存储桶是否存在,如果不存在则创建</span></span><br><span class="line">flag, err := checkBucket(bucketName)</span><br><span class="line"><span class="keyword">if</span> !flag {</span><br><span class="line">fmt.Println(err)</span><br><span class="line"><span class="keyword">return</span> <span class="string">""</span>, err</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 保存的文件名</span></span><br><span class="line">fileName := buildFileName(objectName)</span><br><span class="line"><span class="comment">// 将文件上传到指定的存储桶和对象名</span></span><br><span class="line">_, err = client.PutObject(context.Background(), bucketName, fileName, file, fileHeader.Size, minio.PutObjectOptions{ContentType: <span class="string">"application/octet-stream"</span>})</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line">fmt.Println(err)</span><br><span class="line"><span class="keyword">return</span> <span class="string">""</span>, err</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 返回生成的URL</span></span><br><span class="line"><span class="keyword">return</span> urlPrefix + bucketName + <span class="string">"/"</span> + fileName, <span class="literal">nil</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="调用minioUtil"><a href="#调用minioUtil" class="headerlink" title="调用minioUtil"></a>调用minioUtil</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">UploadFile</span><span class="params">(c *gin.Context)</span></span> {</span><br><span class="line">file, _ := c.FormFile(<span class="string">"file"</span>)</span><br><span class="line">path, err := minioUtil.UploadFile(<span class="string">"test"</span>,file.Filename,file)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"> fmt.Println(path)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> go </category>
</categories>
<tags>
<tag> go </tag>
<tag> minio </tag>
</tags>
</entry>
<entry>
<title>go项目搭建与部署</title>
<link href="/2023/10/10/go%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA%E4%B8%8E%E9%83%A8%E7%BD%B2/"/>
<url>/2023/10/10/go%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA%E4%B8%8E%E9%83%A8%E7%BD%B2/</url>
<content type="html"><![CDATA[<h1 id="go项目搭建与部署"><a href="#go项目搭建与部署" class="headerlink" title="go项目搭建与部署"></a>go项目搭建与部署</h1><p>参考文章:</p><ul><li><a href="https://blog.csdn.net/wykqh/article/details/121861036">Win10打包Go项目部署到Linux上注意事项_go 打包成linux_日日行不惧千万里的博客-CSDN博客</a></li><li><a href="https://blog.csdn.net/humanbeng/article/details/77876989">golang清除go get 的包_golang 删除指定包-CSDN博客</a></li></ul><h2 id="开发环境搭建(Windows10)"><a href="#开发环境搭建(Windows10)" class="headerlink" title="开发环境搭建(Windows10)"></a>开发环境搭建(Windows10)</h2><h3 id="安装goland"><a href="#安装goland" class="headerlink" title="安装goland"></a>安装goland</h3><p>下载链接:<a href="https://www.jetbrains.com/zh-cn/go/download/other.html">其他版本 - GoLand (jetbrains.com)</a></p><h3 id="安装git环境"><a href="#安装git环境" class="headerlink" title="安装git环境"></a>安装git环境</h3><p>下载链接:<a href="https://git-scm.com/download/win">Git - Downloading Package (git-scm.com)</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 控制台检查git是否安装成功</span></span><br><span class="line">D:\Go\WorkSpace> git version</span><br><span class="line">git version 2.36.1.windows.1</span><br></pre></td></tr></table></figure><p><strong>如果想要禁用或忽略 SSL 证书验证</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 禁用或忽略SSL证书验证</span></span><br><span class="line">git config --global http.sslVerify false</span><br></pre></td></tr></table></figure><h3 id="go环境搭建"><a href="#go环境搭建" class="headerlink" title="go环境搭建"></a>go环境搭建</h3><p>打开goland,点击<code>所有设置</code>,选择<code>GOROOT</code>安装go</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 控制台检查go是否安装成功</span></span><br><span class="line">D:\Go\WorkSpace> go version</span><br><span class="line">go version go1.21.2 windows/amd64</span><br></pre></td></tr></table></figure><p>设置go代理</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 查看go环境配置</span></span><br><span class="line">D:\Go\WorkSpace> go env</span><br><span class="line">set GO111MODULE=on</span><br><span class="line">set GOARCH=amd64 </span><br><span class="line">set GOBIN=</span><br><span class="line">......</span><br><span class="line">set GOPROXY=https://goproxy.cn,direct </span><br><span class="line">......</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 设置go代理</span></span><br><span class="line">go env -w GOPROXY=https://goproxy.cn,direct</span><br><span class="line"><span class="meta">#</span><span class="bash"> 或者</span></span><br><span class="line">go env -w GOPROXY=https://goproxy.io,direct</span><br></pre></td></tr></table></figure><h2 id="go项目"><a href="#go项目" class="headerlink" title="go项目"></a>go项目</h2><h3 id="go管理依赖"><a href="#go管理依赖" class="headerlink" title="go管理依赖"></a>go管理依赖</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> go拉取依赖</span></span><br><span class="line">go mod tidy</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> go清除mod缓存</span></span><br><span class="line">go clean -modcache</span><br></pre></td></tr></table></figure><h3 id="go打包"><a href="#go打包" class="headerlink" title="go打包"></a>go打包</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> <span class="built_in">cd</span>到main目录</span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Windows环境</span></span><br><span class="line">go build</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Linux环境</span></span><br><span class="line">SET GOOS=linux</span><br><span class="line">SET GOARCH=amd64</span><br><span class="line">go build</span><br></pre></td></tr></table></figure><h2 id="docker部署"><a href="#docker部署" class="headerlink" title="docker部署"></a>docker部署</h2><h3 id="Dockerfile编写"><a href="#Dockerfile编写" class="headerlink" title="Dockerfile编写"></a>Dockerfile编写</h3><p>编写Dockerfile文件</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 拉取基础镜像</span></span><br><span class="line"><span class="keyword">FROM</span> golang:alpine</span><br><span class="line"><span class="comment"># 作者</span></span><br><span class="line"><span class="keyword">MAINTAINER</span> zhb <zhuhebin0928@<span class="number">163</span>.com></span><br><span class="line"><span class="comment"># 添加打包后的程序</span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> router router</span></span><br><span class="line"><span class="comment"># 配置文件(没有可以不添加)</span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> config/ config/</span></span><br><span class="line"><span class="comment"># 开发端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8090</span></span><br><span class="line"><span class="comment"># 赋予程序执行权限</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> chmod +x router</span></span><br><span class="line"><span class="comment"># 容器启动后执行的命令</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"./router"</span>]</span></span><br></pre></td></tr></table></figure><p>用Dockerfile构建镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 用Dockerfile构建镜像</span></span><br><span class="line">docker build -t mytest .</span><br></pre></td></tr></table></figure><h3 id="docker-compose文件编写"><a href="#docker-compose文件编写" class="headerlink" title="docker-compose文件编写"></a>docker-compose文件编写</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">version: "3"</span><br><span class="line">services:</span><br><span class="line"> nginx:</span><br><span class="line"> image: nginx:latest</span><br><span class="line"> restart: always</span><br><span class="line"> container_name: nginx</span><br><span class="line"> network_mode: "host"</span><br><span class="line"> volumes:</span><br><span class="line"> - /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf</span><br><span class="line"> - /data/:/data/</span><br><span class="line"> mytest:</span><br><span class="line"> image: mytest:latest</span><br><span class="line"> restart: always</span><br><span class="line"> container_name: mytest</span><br><span class="line"> network_mode: "host"</span><br><span class="line"> ports:</span><br><span class="line"> - 8090:8090</span><br><span class="line"> volumes:</span><br><span class="line"> - /data/go/config:/go/config</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> go </category>
</categories>
<tags>
<tag> docker </tag>
<tag> go </tag>
</tags>
</entry>
<entry>
<title>go实现mqtt订阅与推送</title>
<link href="/2023/09/24/go%E5%AE%9E%E7%8E%B0mqtt%E8%AE%A2%E9%98%85%E4%B8%8E%E6%8E%A8%E9%80%81/"/>
<url>/2023/09/24/go%E5%AE%9E%E7%8E%B0mqtt%E8%AE%A2%E9%98%85%E4%B8%8E%E6%8E%A8%E9%80%81/</url>
<content type="html"><![CDATA[<h1 id="go实现mqtt订阅与推送"><a href="#go实现mqtt订阅与推送" class="headerlink" title="go实现mqtt订阅与推送"></a>go实现mqtt订阅与推送</h1><h2 id="搭建mqtt服务端"><a href="#搭建mqtt服务端" class="headerlink" title="搭建mqtt服务端"></a>搭建mqtt服务端</h2><h3 id="安装emqx"><a href="#安装emqx" class="headerlink" title="安装emqx"></a>安装emqx</h3><p>本文安装<code>emqx</code>作为服务端,请自行安装<code>docker</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 拉取emqx最新镜像</span></span><br><span class="line">docker pull emqx</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建并运行emqx容器</span></span><br><span class="line">docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx</span><br></pre></td></tr></table></figure><p>浏览器打开<code>http://ip:18083</code>,账号默认为<code>admin</code>,密码默认为<code>public</code></p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20230922091926.png" alt="emqx控制台"></p><h3 id="安装mqttx"><a href="#安装mqttx" class="headerlink" title="安装mqttx"></a>安装mqttx</h3><p>有需要可以选择安装<code>mqttx</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 拉取mqttx镜像</span></span><br><span class="line">docker pull emqx/mqttx-web</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建并运行mqttx容器</span></span><br><span class="line">docker run -d --name mqttx-web -p 18830:80 emqx/mqttx-web</span><br></pre></td></tr></table></figure><p>浏览器打开<code>http://ip:18830</code></p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20230922094522.png" alt="mqttx页面"></p><h2 id="go创建mqtt客户端"><a href="#go创建mqtt客户端" class="headerlink" title="go创建mqtt客户端"></a>go创建mqtt客户端</h2><h3 id="mqtt配置文件"><a href="#mqtt配置文件" class="headerlink" title="mqtt配置文件"></a>mqtt配置文件</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">mqtt:</span></span><br><span class="line"> <span class="comment"># 地址</span></span><br><span class="line"> <span class="attr">broker:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:1883</span></span><br><span class="line"> <span class="comment"># 账号</span></span><br><span class="line"> <span class="attr">username:</span> <span class="string">admin</span></span><br><span class="line"> <span class="comment"># 密码</span></span><br><span class="line"> <span class="attr">password:</span> <span class="string">public</span></span><br><span class="line"> <span class="comment"># 客户端id</span></span><br><span class="line"> <span class="attr">clientID:</span> <span class="string">go_mqtt_client</span></span><br><span class="line"> <span class="comment"># 超时时间,单位为秒</span></span><br><span class="line"> <span class="attr">timeOut:</span> <span class="number">60</span></span><br><span class="line"> <span class="comment"># 订阅主题</span></span><br><span class="line"> <span class="attr">topicName:</span> <span class="string">testTopic</span></span><br></pre></td></tr></table></figure><h3 id="加载mqtt配置文件"><a href="#加载mqtt配置文件" class="headerlink" title="加载mqtt配置文件"></a>加载mqtt配置文件</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// mqtt配置文件结构体</span></span><br><span class="line"><span class="keyword">type</span> MqttConfig <span class="keyword">struct</span> {</span><br><span class="line">Broker <span class="keyword">string</span> <span class="string">`yaml:"mqtt.broker"`</span></span><br><span class="line">UserName <span class="keyword">string</span> <span class="string">`yaml:"mqtt.username"`</span></span><br><span class="line">Password <span class="keyword">string</span> <span class="string">`yaml:"mqtt.password"`</span></span><br><span class="line">ClientID <span class="keyword">string</span> <span class="string">`yaml:"mqtt.clientID"`</span></span><br><span class="line">TimeOut <span class="keyword">int64</span> <span class="string">`yaml:"mqtt.timeOut"`</span></span><br><span class="line">TopicName <span class="keyword">string</span> <span class="string">`yaml:"mqtt.topicName"`</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// mqtt配置文件</span></span><br><span class="line"><span class="keyword">var</span> mqttConfig *MqttConfig</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载mqtt配置文件</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loadMqttConfig</span><span class="params">()</span> *<span class="title">MqttConfig</span></span> {</span><br><span class="line">mqttConfig := MqttConfig{</span><br><span class="line">Broker: viper.GetString(<span class="string">"mqtt.broker"</span>),</span><br><span class="line">UserName: viper.GetString(<span class="string">"mqtt.username"</span>),</span><br><span class="line">Password: viper.GetString(<span class="string">"mqtt.password"</span>),</span><br><span class="line">ClientID: viper.GetString(<span class="string">"mqtt.clientID"</span>),</span><br><span class="line">TimeOut: viper.GetInt64(<span class="string">"mqtt.timeOut"</span>),</span><br><span class="line">TopicName: viper.GetString(<span class="string">"mqtt.topicName"</span>),</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> &mqttConfig</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</span><br><span class="line">mqttConfig = loadMqttConfig()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取mqtt配置文件</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetMqttConfig</span><span class="params">()</span> *<span class="title">MqttConfig</span></span> {</span><br><span class="line"><span class="keyword">return</span> mqttConfig</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="设置mqtt客户端"><a href="#设置mqtt客户端" class="headerlink" title="设置mqtt客户端"></a>设置mqtt客户端</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// mqtt客户端</span></span><br><span class="line"><span class="keyword">var</span> client mqtt.Client</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置mqtt客户端,并进行连接</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">SetMqttClient</span><span class="params">(mqttConfig *config.MqttConfig)</span></span> {</span><br><span class="line"><span class="comment">// 配置信息</span></span><br><span class="line">clientOptions := mqtt.NewClientOptions().AddBroker(mqttConfig.Broker).SetUsername(mqttConfig.UserName).SetPassword(mqttConfig.Password)</span><br><span class="line">clientOptions.SetClientID(mqttConfig.ClientID)</span><br><span class="line">clientOptions.SetConnectTimeout(time.Duration(mqttConfig.TimeOut) * time.Second)</span><br><span class="line"><span class="comment">// 创建客户端</span></span><br><span class="line">client = mqtt.NewClient(clientOptions)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 连接</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Connect</span><span class="params">(timeOut <span class="keyword">int64</span>)</span></span> {</span><br><span class="line"><span class="comment">// 判断客户端连接状态</span></span><br><span class="line"><span class="keyword">if</span> token := client.Connect(); token.WaitTimeout(time.Duration(timeOut) * time.Second) && token.Wait() && token.Error() != <span class="literal">nil</span> {</span><br><span class="line"><span class="built_in">panic</span>(token.Error())</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 订阅主题</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">DoSubscribe</span><span class="params">(topic <span class="keyword">string</span>, doMessage mqtt.MessageHandler)</span></span> {</span><br><span class="line"><span class="keyword">for</span> {</span><br><span class="line">token := client.Subscribe(topic, <span class="number">1</span>, doMessage)</span><br><span class="line">token.Wait()</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="推送数据"><a href="#推送数据" class="headerlink" title="推送数据"></a>推送数据</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 推送数据</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">DoPublish</span><span class="params">(client mqtt.Client, topicName <span class="keyword">string</span>, content <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line"><span class="keyword">for</span> {</span><br><span class="line"><span class="comment">// 往主题推送数据</span></span><br><span class="line">client.Publish(topicName, <span class="number">1</span>, <span class="literal">false</span>, content)</span><br><span class="line">time.Sleep(time.Duration(<span class="number">3</span>) * time.Second)</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// 获取mqtt配置</span></span><br><span class="line">mqttConfig := config.GetMqttConfig()</span><br><span class="line"> <span class="comment">// 设置mqtt客户端</span></span><br><span class="line">MqttUtil.SetMqttClient(mqttConfig)</span><br><span class="line"><span class="keyword">go</span> MqttUtil.DoPublish(mqttConfig.TopicName, <span class="string">"testPublish..."</span>)</span><br><span class="line">MqttUtil.Connect(mqttConfig.TimeOut)</span><br><span class="line"> <span class="comment">// 订阅主题,从主题中获取数据</span></span><br><span class="line"> <span class="comment">// 参数:主题name、回调函数</span></span><br><span class="line">MqttUtil.DoSubscribe(mqttConfig.TopicName, <span class="function"><span class="keyword">func</span><span class="params">(client mqtt.Client, msg mqtt.Message)</span></span> {</span><br><span class="line">fmt.Printf(<span class="string">"[%s] -> %s\n"</span>, msg.Topic(), msg.Payload())</span><br><span class="line">})</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> go </category>
</categories>
<tags>
<tag> go </tag>
<tag> mqtt </tag>
</tags>
</entry>
<entry>
<title>使用docker搭建项目</title>
<link href="/2023/09/10/%E4%BD%BF%E7%94%A8docker%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE/"/>
<url>/2023/09/10/%E4%BD%BF%E7%94%A8docker%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE/</url>
<content type="html"><![CDATA[<h1 id="使用docker搭建项目"><a href="#使用docker搭建项目" class="headerlink" title="使用docker搭建项目"></a>使用docker搭建项目</h1><h2 id="安装docker"><a href="#安装docker" class="headerlink" title="安装docker"></a>安装docker</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 更新源</span></span><br><span class="line">yum -y update</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> centos一键安装docker</span></span><br><span class="line">curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查询docker版本</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 提示Docker version 24.0.0, build 98fdcd7就算安装成功</span></span><br><span class="line">docker -v</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 启动docker</span></span><br><span class="line">systemctl start docker</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看docker运行状态</span></span><br><span class="line">systemctl status docker</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 设置docker自启动</span></span><br><span class="line">systemctl enable docker</span><br></pre></td></tr></table></figure><h2 id="docker换源"><a href="#docker换源" class="headerlink" title="docker换源"></a>docker换源</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 查看docker信息</span></span><br><span class="line">docker info</span><br><span class="line"><span class="meta">#</span><span class="bash"> Client: Docker Engine - Community</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Version: 24.0.5</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> ......</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Insecure Registries:</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 127.0.0.0/8</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Live Restore Enabled: <span class="literal">false</span></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建或编辑daemon.json文件</span></span><br><span class="line">vi /etc/docker/daemon.json</span><br></pre></td></tr></table></figure><p>修改daemon.json</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在daemon.json文件中修改如下</span></span><br><span class="line">{</span><br><span class="line"> <span class="attr">"registry-mirrors"</span>:[<span class="string">"https://pee6w651.mirror.aliyuncs.com"</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>保存daemon.json</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 重启docker</span></span><br><span class="line">systemctl restart docker</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看docker信息</span></span><br><span class="line">docker info</span><br><span class="line"><span class="meta">#</span><span class="bash"> Client: Docker Engine - Community</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Version: 24.0.5</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> ......</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Insecure Registries:</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 127.0.0.0/8</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Registry Mirrors:</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> https://pee6w651.mirror.aliyuncs.com/</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Live Restore Enabled: <span class="literal">false</span></span></span><br></pre></td></tr></table></figure><h2 id="运行nginx容器"><a href="#运行nginx容器" class="headerlink" title="运行nginx容器"></a>运行nginx容器</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 拉取nginx镜像</span></span><br><span class="line">docker pull nginx</span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建对应的文件夹</span></span><br><span class="line">mkdir -p /root/nginx/conf</span><br><span class="line"><span class="meta">#</span><span class="bash"> 运行容器</span></span><br><span class="line">docker run --name nginx -p 80:80 -d nginx</span><br><span class="line"><span class="meta">#</span><span class="bash"> 将容器中的文件导出</span></span><br><span class="line">docker cp nginx:/etc/nginx/nginx.conf /root/nginx/conf/nginx.conf</span><br><span class="line"><span class="meta">#</span><span class="bash"> 删除容器</span></span><br><span class="line">docker ps -a</span><br><span class="line">docker stop nginx</span><br><span class="line">docker rm nginx</span><br><span class="line"><span class="meta">#</span><span class="bash"> 运行容器</span></span><br><span class="line">docker run -p 80:80 --name nginx -v /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /data/:/data/ -d nginx:latest</span><br></pre></td></tr></table></figure><h2 id="封装镜像"><a href="#封装镜像" class="headerlink" title="封装镜像"></a>封装镜像</h2><p>编写Dockerfile</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建并编写Dockerfile</span></span><br><span class="line">vi Dockerfile</span><br><span class="line"><span class="comment"># 拉取openjdk8作为基础镜像</span></span><br><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">8</span></span><br><span class="line"><span class="comment"># 作者</span></span><br><span class="line"><span class="keyword">MAINTAINER</span> zhb <zhuhebin0928@<span class="number">163</span>.com></span><br><span class="line"><span class="comment"># 添加jar到镜像并重命名</span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> mytest.jar mytest.jar</span></span><br><span class="line"><span class="comment"># 镜像启动后暴露的端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8090</span></span><br><span class="line"><span class="comment"># jar运行命令,参数使用逗号隔开</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"java"</span>,<span class="string">"-jar"</span>,<span class="string">"mytest.jar"</span>]</span></span><br></pre></td></tr></table></figure><p>构建docker镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker build -t mytest .</span><br></pre></td></tr></table></figure><p>创建并运行容器</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d --name mytest -p 8090:8090 mytest</span><br></pre></td></tr></table></figure><p>导出镜像(导出容器再导入可能会出问题)</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker save -o mytest.tar mytest</span><br></pre></td></tr></table></figure><p>导入镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker load -i mytest.tar</span><br></pre></td></tr></table></figure><p>重新启动容器</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d --name mytest -p 8090:8090 mytest</span><br></pre></td></tr></table></figure><h2 id="安装docker-compose"><a href="#安装docker-compose" class="headerlink" title="安装docker-compose"></a>安装docker-compose</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 建议去官网下载最新版!!!</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> https://docs.docker.com/compose/install/standalone/</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 下载docker-compose文件</span></span><br><span class="line">curl -L "https://get.daocloud.io/docker/compose/releases/download/v2.2.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose</span><br><span class="line"><span class="meta">#</span><span class="bash"> 或者</span></span><br><span class="line">curl -SL "https://github.com/docker/compose/releases/download/v2.20.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 添加可执行权限</span></span><br><span class="line">sudo chmod +x /usr/local/bin/docker-compose</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 测试安装结果</span></span><br><span class="line">docker-compose --version</span><br><span class="line"><span class="meta">#</span><span class="bash"> 或者</span></span><br><span class="line">docker-compose -v</span><br></pre></td></tr></table></figure><h2 id="使用docker-compose"><a href="#使用docker-compose" class="headerlink" title="使用docker-compose"></a>使用docker-compose</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 创建docker-compose.yml文件</span></span><br><span class="line">touch docker-compose.yml</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 编写对应的docker-compose.yml文件</span></span><br><span class="line">vi docker-compose.yml</span><br><span class="line">version: "3"</span><br><span class="line">services:</span><br><span class="line"> mytest:</span><br><span class="line"> image: mytest:latest</span><br><span class="line"> restart: always</span><br><span class="line"> container_name: mytest</span><br><span class="line"> network_mode: "host"</span><br><span class="line"> ports:</span><br><span class="line"> - 8090:8090</span><br><span class="line"> nginx:</span><br><span class="line"> image: nginx:latest</span><br><span class="line"> restart: always</span><br><span class="line"> container_name: nginx</span><br><span class="line"> network_mode: "host"</span><br><span class="line"> volumes:</span><br><span class="line"> - /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf</span><br><span class="line"> - /data/:/data/</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建并启动全部容器</span></span><br><span class="line">docker-compose up -d</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看全部容器</span></span><br><span class="line">docker-compose ps</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> docker </category>
</categories>
<tags>
<tag> docker </tag>
</tags>
</entry>
<entry>
<title>使用nginx转发websocket</title>
<link href="/2023/08/02/%E4%BD%BF%E7%94%A8nginx%E8%BD%AC%E5%8F%91websocket/"/>
<url>/2023/08/02/%E4%BD%BF%E7%94%A8nginx%E8%BD%AC%E5%8F%91websocket/</url>
<content type="html"><![CDATA[<p>将<code>wss://localhost/wssSocket</code>转发到<code>ws://127.0.0.1:8090/websocket/statistic</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">location /wssSocket {</span><br><span class="line"> proxy_pass http://127.0.0.1:8090/websocket/statistic;</span><br><span class="line"> proxy_http_version 1.1;</span><br><span class="line"> proxy_redirect off;</span><br><span class="line"> proxy_set_header Upgrade $http_upgrade;</span><br><span class="line"> proxy_set_header Connection "upgrade";</span><br><span class="line"> proxy_set_header Host $host;</span><br><span class="line"> proxy_set_header X-Real-IP $remote_addr;</span><br><span class="line"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line"> proxy_set_header X-Forwarded-Host $server_name;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> nginx </category>
</categories>
<tags>
<tag> nginx </tag>
<tag> websocket </tag>
</tags>
</entry>
<entry>
<title>如何给docker扩容</title>
<link href="/2023/06/07/%E5%A6%82%E4%BD%95%E7%BB%99docker%E6%89%A9%E5%AE%B9/"/>
<url>/2023/06/07/%E5%A6%82%E4%BD%95%E7%BB%99docker%E6%89%A9%E5%AE%B9/</url>
<content type="html"><![CDATA[<h1 id="如何给docker扩容"><a href="#如何给docker扩容" class="headerlink" title="如何给docker扩容"></a>如何给docker扩容</h1><p>参考文章:</p><ol><li><a href="https://www.cnblogs.com/mihoutao/p/13826374.html">动态扩容Linux根目录:/dev/mapper/centos-home分配部分空间给/dev/mapper/centos-root(/dev/mapper/centos-root经常会满,可是/dev/mapper/centos-home很空) - 金龟子大战猕猴桃 - 博客园 (cnblogs.com)</a></li><li><a href="https://blog.csdn.net/qq_42595452/article/details/125061607">Docker关闭不掉进程,Stopping docker.service, but it can still be activated by: docker.socket_warning: stopping httpd.service, but it can still _爱吃醋的小可爱的博客-CSDN博客</a></li><li><a href="https://www.cnblogs.com/Sungeek/p/11857549.html">Linux中fuser命令用法详解 - Tse先生 - 博客园 (cnblogs.com)</a></li><li><a href="https://blog.csdn.net/adanjeep/article/details/123480257">问题:INTERNAL ERROR: cannot create temporary directory及解决方法_cannot creat tempory-CSDN博客</a></li></ol><h2 id="具体步骤"><a href="#具体步骤" class="headerlink" title="具体步骤"></a>具体步骤</h2><p>在阅读本文时,笔者默认你已安装<code>docker</code>和<code>docker-compose</code>。</p><ol><li><p>请切换至<code>root</code>用户,避免后续操作缺少相应的权限。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 切换至root用户,然后输入密码</span></span><br><span class="line">su root</span><br></pre></td></tr></table></figure></li><li><p>查看当前磁盘使用情况。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 查看磁盘</span></span><br><span class="line">df -h</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 文件系统 容量 已用 可用 已用% 挂载点</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> devtmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> /dev/mapper/centos-root 50G 50G 16k 100% /</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> /dev/sda1</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> /dev/mapper/centos-home 957G 33M 957G 1% /home</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> overlay 50G 50G 16k 100% /data/lib/docker/overlay2/...</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> overlay 50G 50G 16k 100% /data/lib/docker/overlay2/...</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> ......</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br></pre></td></tr></table></figure><p>重点关注<code>/dev/mapper/centos-root</code>和<code>/dev/mapper/centos-home</code>两个文件系统。<span style='color:red;font-size:20px'>如果你的服务器上的<code>/dev/mapper/centos-root</code>和<code>/dev/mapper/centos-home</code>叫做别的名称,请将下文中的<code>/dev/mapper/centos-root</code>和<code>/dev/mapper/centos-home</code>替换成你服务器上对应的名称</span>。</p></li><li><p>扩容思路</p><p>文件系统<code>/dev/mapper/centos-root</code>对应<code>/</code>目录,文件系统<code>/dev/mapper/centos-home</code>对应<code>/home</code>目录。将<code>/home</code>文件夹备份,删除<code>/home</code>文件系统所在的逻辑卷,增大<code>/</code>文件系统所在的逻辑卷,增大<code>/</code>文件系统大小,最后新建<code>/home</code>目录,并恢复<code>/home</code>文件夹下的内容。</p></li><li><p>关闭<code>docker</code>容器。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 关闭docker容器</span></span><br><span class="line">docker-compose stop</span><br></pre></td></tr></table></figure><p><strong>如果提示<code>INTERNAL ERROR: cannot create temporary directory</code><strong>,表明<code>docker</code>磁盘已经用完,可以使用<code>docker system prune</code>清理关闭的容器、无用的数据卷、网络和虚悬镜像,本文不对此进行赘述,</strong>请直接执行下一步的操作</strong>。</p></li><li><p>关闭<code>docker</code>服务。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 关闭docker服务</span></span><br><span class="line">systemctl stop docker</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看docker状态</span></span><br><span class="line">systemctl status docker</span><br></pre></td></tr></table></figure><p>如果输入<code>systemctl stop docker</code>后提示:<code>Warning: Stopping docker.service, but it can still be activated by: docker.socket</code>,请先关闭<code>docker</code>自动唤醒机制,然后再关闭<code>docker</code>服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 关闭docker自动唤醒</span></span><br><span class="line">systemctl stop docker.socket</span><br></pre></td></tr></table></figure></li><li><p>备份源目录文件。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 将/home目录打包放到/run目录下,也可以放在别的目录下,请保证该目录下磁盘空间充足</span></span><br><span class="line">tar cvf /run/home.tar /home</span><br></pre></td></tr></table></figure></li><li><p>终止源目录下的进程。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 终止/home目录下的进程</span></span><br><span class="line">fuser -km /home</span><br></pre></td></tr></table></figure><p>如果提示<code>fuser</code>命令不存在,如果安装<code>fuser</code>提示磁盘不足请重试</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 安装fuser</span></span><br><span class="line">yum install -y psmisc</span><br></pre></td></tr></table></figure></li><li><p>卸载源目录。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 卸载/home目录</span></span><br><span class="line">umount /home</span><br></pre></td></tr></table></figure></li><li><p>删除源目录对应的逻辑卷。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 删除/home目录对应的/dev/mapper/centos-home逻辑卷</span></span><br><span class="line">lvremove /dev/mapper/centos-home</span><br></pre></td></tr></table></figure></li><li><p>扩容目标逻辑卷。</p></li></ol> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 给/dev/mapper/centos-root数据卷扩容100g</span></span><br><span class="line">lvextend -L +100G /dev/mapper/centos-root</span><br></pre></td></tr></table></figure><ol start="11"><li><p>扩大目标文件系统。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 扩大/dev/mapper/centos-root文件系统</span></span><br><span class="line">xfs_growfs /dev/mapper/centos-root</span><br></pre></td></tr></table></figure></li><li><p>重建源目录对应的逻辑卷。</p><p><strong>请注意,此处的大小800G,最好是填写原大小减去为<code>centos-root</code>扩容的大小</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 重建/dev/mapper/centos-home逻辑卷</span></span><br><span class="line">lvcreate -L 800G -n/dev/mapper/centos-home</span><br></pre></td></tr></table></figure></li><li><p>重建源文件系统。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 创建/dev/mapper/centos-home文件系统</span></span><br><span class="line">mkfs.xfs /dev/mapper/centos-home</span><br></pre></td></tr></table></figure></li><li><p>重新挂载源目录。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 挂载/dev/mapper/centos-home目录</span></span><br><span class="line">mount /dev/mapper/centos-home</span><br></pre></td></tr></table></figure></li><li><p>恢复源目录中的内容。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 将备份文件解压</span></span><br><span class="line">tar xvf /run/home.tar -C /</span><br></pre></td></tr></table></figure></li><li><p>再次查看当前磁盘使用情况。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 查看磁盘</span></span><br><span class="line">df -h</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 文件系统 容量 已用 可用 已用% 挂载点</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> devtmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> /dev/mapper/centos-root 150G 50G 100G 33% /</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> /dev/sda1</span> </span><br><span class="line"><span class="meta">#</span><span class="bash"> /dev/mapper/centos-home 800G 33M 800G 1% /home</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> overlay 50G 50G 16k 100% /data/lib/docker/overlay2/...</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> overlay 50G 50G 16k 100% /data/lib/docker/overlay2/...</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> ......</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> tmpfs</span> </span><br></pre></td></tr></table></figure></li><li><p>重启服务器(如果重启<code>docker</code>成功则跳过,如果失败请重启服务器)。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 重启服务器</span></span><br><span class="line">reboot</span><br></pre></td></tr></table></figure></li><li><p>重启<code>docker</code>服务。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 开启docker服务</span></span><br><span class="line">systemctl start docker</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看docker状态</span></span><br><span class="line">systemctl status docker</span><br></pre></td></tr></table></figure><p>如果之前关闭了<code>docker</code>自动唤醒机制,则进行恢复</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 开启docker自动唤醒</span></span><br><span class="line">systemctl start docker.socket</span><br></pre></td></tr></table></figure></li><li><p>重启所有<code>docker</code>容器。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose restart</span><br></pre></td></tr></table></figure></li><li><p>删除<code>/run</code>下面的备份。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rm -rf /run/home.tar</span><br></pre></td></tr></table></figure></li></ol>]]></content>
<categories>
<category> docker </category>
</categories>
<tags>
<tag> docker </tag>
</tags>
</entry>
<entry>
<title>IP转地址</title>
<link href="/2023/05/12/IP%E8%BD%AC%E5%9C%B0%E5%9D%80/"/>
<url>/2023/05/12/IP%E8%BD%AC%E5%9C%B0%E5%9D%80/</url>
<content type="html"><![CDATA[<h1 id="IP转地址"><a href="#IP转地址" class="headerlink" title="IP转地址"></a>IP转地址</h1><p>官方链接:</p><ul><li>github:<a href="https://github.com/lionsoul2014/ip2region">Ip2region</a></li><li>gitee:<a href="https://gitee.com/lionsoul/ip2region">ip2region</a></li></ul><p>参考文章:</p><ul><li><a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">ip2region 是一个离线 IP 数据管理框架和定位库</a></li><li><a href="https://zhuanlan.zhihu.com/p/270555252">深入浅出之ip2region实现 - 知乎 (zhihu.com)</a></li></ul><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>ip2region v2.0 - 是一个离线IP地址定位库和IP定位数据管理框架,10微秒级别的查询效率,提供了众多主流编程语言的 <code>xdb</code> 数据生成和查询客户端实现。每个 ip 数据段的 region 信息都固定了格式:<code>国家|区域|省份|城市|ISP(网络服务提供商)</code></p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="获取离线包"><a href="#获取离线包" class="headerlink" title="获取离线包"></a>获取离线包</h3><p>从网站上获取ip2region离线包,并将<code>ip2region.xdb</code>放在<code>resource</code>目录下</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/1.png" alt="获取离线包"></p><h3 id="导入依赖"><a href="#导入依赖" class="headerlink" title="导入依赖"></a>导入依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- ip2region依赖(必须) --></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.lionsoul<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>ip2region<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.6.5<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- hutool依赖(非必须) --></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>cn.hutool<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>hutool-all<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>5.8.4<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><h3 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cn.hutool.core.io.resource.ResourceUtil;</span><br><span class="line"><span class="keyword">import</span> org.lionsoul.ip2region.xdb.Searcher;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span 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>{</span><br><span class="line"> <span class="comment">// 使用hutool的ResourceUtil从resource目录下读取ip2region.xdb文件</span></span><br><span class="line"> <span class="keyword">byte</span>[] bytes = ResourceUtil.readBytes(<span class="string">"ip2region.xdb"</span>);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 创建一个完全基于内存的查询对象</span></span><br><span class="line"> Searcher searcher = Searcher.newWithBuffer(bytes);</span><br><span class="line"> <span class="comment">// 查询ip,返回值:国家|区域|省份|城市|ISP(网络服务提供商)</span></span><br><span class="line"> System.out.println(searcher.search(<span class="string">"1.2.3.4"</span>));</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 控制台打印:</span></span><br><span class="line"><span class="comment">// 美国|0|华盛顿|0|谷歌</span></span><br></pre></td></tr></table></figure><h3 id="工具类封装"><a href="#工具类封装" class="headerlink" title="工具类封装"></a>工具类封装</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cn.hutool.core.io.resource.ResourceUtil;</span><br><span class="line"><span class="keyword">import</span> lombok.SneakyThrows;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"><span class="keyword">import</span> org.lionsoul.ip2region.xdb.Searcher;</span><br><span class="line"><span class="keyword">import</span> javax.annotation.PostConstruct;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">IpUtils</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 一个完全基于内存的查询对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Searcher searcher;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 默认返回值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String UNKNOWN_ADDRESS = <span class="string">"UNKNOWN"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 初始化</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@PostConstruct</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> searcher = Searcher.newWithBuffer(ResourceUtil.readBytes(<span class="string">"ip2region.xdb"</span>));</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"failed to create content cached searcher"</span>, e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * IP转地址</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ip ip</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 地址</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">ipToAddress</span><span class="params">(String ip)</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> searcher.search(ip);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> <span class="keyword">return</span> UNKNOWN_ADDRESS;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 工具类 </tag>
</tags>
</entry>
<entry>
<title>HyperLogLog学习</title>
<link href="/2022/12/31/HyperLogLog%E5%AD%A6%E4%B9%A0/"/>
<url>/2022/12/31/HyperLogLog%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<h1 id="HyperLogLog学习"><a href="#HyperLogLog学习" class="headerlink" title="HyperLogLog学习"></a>HyperLogLog学习</h1><p>本文参考文章:</p><p><a href="https://blog.csdn.net/qq_33256688/article/details/81947641?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-81947641-blog-77247649.pc_relevant_layerdownloadsortv1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-81947641-blog-77247649.pc_relevant_layerdownloadsortv1&utm_relevant_index=1">探索HyperLogLog算法(含Java实现)_燃烧杯的博客-CSDN博客</a></p><p><a href="https://cn.kyligence.io/blog/count-distinct-hyperloglog/">大数据分析常用去重算法分析『HyperLogLog 篇』 (kyligence.io)</a></p><p><a href="http://blog.codinglabs.org/articles/algorithms-for-cardinality-estimation-part-iv.html">CodingLabs - 解读Cardinality Estimation算法(第四部分:HyperLogLog Counting及Adaptive Counting)</a></p><p><a href="https://blog.csdn.net/firenet1/article/details/77247649">神奇的HyperLogLog算法【转载 #涉及到数学原理】_GDRetop的博客-CSDN博客_hyperloglog原理</a></p><p><a href="https://juejin.cn/post/6844903785744056333#heading-2">HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的 - 掘金 (juejin.cn)</a></p><p>论文原文:</p><p><a href="http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf">HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm</a></p><p>HyperLogLog仿真网站:</p><p><a href="http://content.research.neustar.biz/blog/hll.html">Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure – AK Tech Blog (neustar.biz)</a></p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>如果你负责开发维护一个大型的网站,现在需要网站每个网页每天的 UV 数据,然后让你来开发这个统计模块,你会如何实现?</p><p>如果统计 PV 只需要每个网页一个独立的 Redis 计数器就行,这个计数器的 key 后缀加上当天的日期。这样来一个请求,incrby 一次,最终就可以统计出所有的 PV 数据。</p><p>但是 UV 不一样,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的 ID,无论是登陆用户还是未登陆用户都需要一个唯一 ID 来标识。</p><p>你也许已经想到了一个简单的方案,那就是为每一个页面一个独立的 set 集合来存储所有当天访问过此页面的用户 ID。当一个请求过来时,我们使用 sadd 将用户 ID 塞进去就可以了。通过 scard 可以取出这个集合的大小,这个数字就是这个页面的 UV 数据。没错,这是一个非常简单的方案。</p><p>但是,如果你的页面访问量非常大,比如一个爆款页面几千万的 UV,你需要一个很大的 set 集合来统计,这就非常浪费空间。如果这样的页面很多,那所需要的存储空间是惊人的。为这样一个去重功能就耗费这样多的存储空间,值得么?其实需要的数据又不需要太精确,105w 和 106w 这两个数字并没有多大区别,So,有没有更好的解决方案呢?</p><h2 id="HashMap"><a href="#HashMap" class="headerlink" title="HashMap"></a>HashMap</h2><p><code>Java</code>中的<code>HashSet</code>本质是<code>Value</code>值相同的<code>HashMap</code>,假设HashMap的Key为String,Value为bool,当网页有百万人访问时,此HashMap所占用的内存空间为:100万 *(String + bool),而这还只是一个网页的占用。</p><p>占用太大,基本不用考虑。</p><h2 id="B树"><a href="#B树" class="headerlink" title="B树"></a>B树</h2><p>B树最大的优势是插入和查找效率很高,如果用B树存储要统计的数据,可以快速判断新来的数据是否已经存在,并快速将元素插入B树。要计算基数值,只需要计算B树的节点个数。 将B树结构维护到内存中,可以快速统计和计算,但依然存在问题,B树结构只是加快了查找和插入效率,并没有节省存储内存。例如要同时统计几万个链接的UV,每个链接的访问量都很大,如果把这些数据都维护到内存中,实在是够呛。</p><h2 id="bitmap"><a href="#bitmap" class="headerlink" title="bitmap"></a>bitmap</h2><p>bitmap可以理解为通过一个bit数组来存储特定数据的一种数据结构,每一个bit位都能独立包含信息,bit是数据的最小存储单位,因此能大量节省空间,也可以将整个bit数据一次性load到内存计算。 如果定义一个很大的bit数组,基数统计中每一个元素对应到bit数组的其中一位,例如bit数组 <code>001101001</code>代表实际数组<code>[2,3,5,8]</code>。新加入一个元素,只需要将已有的bit数组和新加入的数字做<code>按位或 (or)</code>计算。bitmap中1的数量就是集合的基数值。</p><p>bitmap有一个很明显的优势是可以轻松合并多个统计结果,只需要对多个结果求异或就可以。也可以大大减少存储内存,可以做个简单的计算,如果要统计1亿个数据的基数值,大约需要内存:<br>$$<br>100000000/8/1024/1024 \approx12M<br>$$<br>如果用64(或者32)bit的long(或者int)代表每个统计数据,大约需要内存:<br>$$<br>64(或者32)*100000000/8/1024/1024 \approx 762(或者381)M<br>$$<br>bitmap对于内存的节约量是显而易见的,但还是不够。统计一个对象的基数值需要12M,如果统计10000个对象,就需要将近120G了,同样不能广泛用于大数据场景。</p><h2 id="概率算法"><a href="#概率算法" class="headerlink" title="概率算法"></a>概率算法</h2><p>实际上目前还没有发现更好的在大数据场景中准确计算基数的高效算法,因此在不追求绝对准确的情况下,使用概率算法算是一个不错的解决方案。概率算法不直接存储数据集合本身,通过一定的概率统计方法预估基数值,这种方法可以大大节省内存,同时保证误差控制在一定范围内。目前用于基数计数的概率算法包括:</p><ul><li><p>Linear Counting(LC):早期的基数估计算法,LC在空间复杂度方面并不算优秀,实际上LC的空间复杂度与上文中简单bitmap方法是一样的(但是有个常数项级别的降低),都是<br>$$<br>O(N_{max})<br>$$</p></li><li><p>LogLog Counting(LLC):LogLog Counting相比于LC更加节省内存,空间复杂度只有<br>$$<br>O(log_2(log_2(N_{max})))<br>$$</p></li><li><p>HyperLogLog Counting(HLL):HyperLogLog Counting是基于LLC的优化和改进,在同样空间复杂度情况下,能够比LLC的基数估计误差更小。</p></li></ul><h2 id="HyperLogLog"><a href="#HyperLogLog" class="headerlink" title="HyperLogLog"></a>HyperLogLog</h2><p>上面我们计算过用bitmap存储1亿个统计数据大概需要12M内存;而在HLL中,只需要不到1K内存就能做到;redis中实现的HyperLogLog,只需要12K内存,在标准误差0.81%的前提下,能够统计<code>2^64</code>个数据。那么概率算法是怎样做到如此节省内存的,又是怎样控制误差的呢?</p><h3 id="伯努利实验"><a href="#伯努利实验" class="headerlink" title="伯努利实验"></a>伯努利实验</h3><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/8.png" alt="伯努利实验"></p><p>最终结合极大似然估算的方法,发现在<code>n</code>和<code>kmax</code>中存在估算关联:<code>n = 2^(Kmax)</code> 。这种通过局部信息预估整体数据流特性的方法似乎有些超出我们的基本认知,需要用概率和统计的方法才能推导和验证这种关联关系。</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/9.png" alt="n次伯努利实验"></p><h3 id="分桶平均"><a href="#分桶平均" class="headerlink" title="分桶平均"></a>分桶平均</h3><p>HLL的基本思想是利用集合中数字的比特串第一个1出现位置的最大值来预估整体基数,但是这种预估方法存在较大误差,为了改善误差情况,HLL中引入分桶平均的概念。<br>同样举抛硬币的例子,如果只有一组抛硬币实验,运气较好,第一次实验过程就抛了10次才第一次抛到正面,显然根据公式推导得到的实验次数的估计误差较大;如果100个组同时进行抛硬币实验,同时运气这么好的概率就很低了,每组分别进行多次抛硬币实验,并上报各自实验过程中抛到正面的抛掷次数的最大值,就能根据100组的平均值预估整体的实验次数了。</p><p>具体来说,就是将哈希空间平均分成m份,每份称之为一个桶(bucket)。对于每一个元素,其哈希值的前k比特作为桶编号,其中 2k=m ,而后L-k个比特作为真正用于基数估计的比特串。</p><p>桶编号相同的元素被分配到同一个桶,在进行基数估计时,首先计算每个桶内元素最大的第一个“1”的位置,设为M[i],然后对这m个值取平均后再进行估计,</p><p>下面举一个例子说明分桶平均怎么做。</p><p>假设H的哈希长度为16bit,分桶数m定为32。</p><p>设一个元素哈希值的比特串为“0001001010001010”,由于m为32,因此前5个bit为桶编号,所以这个元素应该归入“00010”即2号桶(桶编号从0开始,最大编号为m-1)</p><p>而剩下部分是“01010001010”且显然ρ(01010001010)=2,所以桶编号为“00010”的元素最大的ρ即为M[2]的值。</p><h3 id="调和平均数"><a href="#调和平均数" class="headerlink" title="调和平均数"></a>调和平均数</h3><p>LLC中使用几何平均数预估整体的基数值,但是当统计数据量较小时误差较大;HLL在LLC基础上做了改进,采用调和平均数,调和平均数的优点是可以过滤掉不健康的统计值,具体的计算公式为:<br>$$<br>H = \frac{n}{\frac{1}{x_1}+\frac{1}{x_2}+…+\frac{1}{x_n}} = \frac{n}{\sum_{i=1}^{n}\frac{1}{x_i}}<br>$$</p><h3 id="偏差修正"><a href="#偏差修正" class="headerlink" title="偏差修正"></a>偏差修正</h3><p>虽然调和平均数能够适当修正算法误差,但作者给出一种分阶段修正算法。当HLL算法开始统计数据时,统计数组中大部分位置都是空数据,并且需要一段时间才能填满数组,这种阶段引入一种小范围修正方法;当HLL算法中统计数组已满的时候,需要统计的数据基数很大,这时候hash空间会出现很多碰撞情况,这种阶段引入一种大范围修正方法。最终算法用伪代码可以表示为如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">m = <span class="number">2</span>^b <span class="comment"># with b in [4...16]</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> m == <span class="number">16</span>:</span><br><span class="line"> alpha = <span class="number">0.673</span></span><br><span class="line"><span class="keyword">elif</span> m == <span class="number">32</span>:</span><br><span class="line"> alpha = <span class="number">0.697</span></span><br><span class="line"><span class="keyword">elif</span> m == <span class="number">64</span>:</span><br><span class="line"> alpha = <span class="number">0.709</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> alpha = <span class="number">0.7213</span>/(<span class="number">1</span> + <span class="number">1.079</span>/m)</span><br><span class="line"> </span><br><span class="line">registers = [<span class="number">0</span>]*m <span class="comment"># initialize m registers to 0</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">###########################################################################</span></span><br><span class="line"><span class="comment"># Construct the HLL structure</span></span><br><span class="line"><span class="keyword">for</span> h <span class="keyword">in</span> hashed(data):</span><br><span class="line"> register_index = <span class="number">1</span> + get_register_index( h,b ) <span class="comment"># binary address of the rightmost b bits</span></span><br><span class="line"> run_length = run_of_zeros( h,b ) <span class="comment"># length of the run of zeroes starting at bit b+1</span></span><br><span class="line"> registers[ register_index ] = <span class="built_in">max</span>( registers[ register_index ], run_length )</span><br><span class="line"><span class="comment">##########################################################################</span></span><br><span class="line"><span class="comment"># Determine the cardinality</span></span><br><span class="line">DV_est = alpha * m^<span class="number">2</span> * <span class="number">1</span>/<span class="built_in">sum</span>( <span class="number">2</span>^ -register ) <span class="comment"># the DV estimate</span></span><br><span class="line"><span class="keyword">if</span> DV_est < <span class="number">5</span>/<span class="number">2</span> * m: <span class="comment"># small range correction</span></span><br><span class="line"> V = count_of_zero_registers( registers ) <span class="comment"># the number of registers equal to zero</span></span><br><span class="line"> <span class="keyword">if</span> V == <span class="number">0</span>: <span class="comment"># if none of the registers are empty, use the HLL estimate</span></span><br><span class="line"> DV = DV_est</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> DV = m * log(m/V) <span class="comment"># i.e. balls and bins correction</span></span><br><span class="line"><span class="keyword">if</span> DV_est <= ( <span class="number">1</span>/<span class="number">30</span> * <span class="number">2</span>^<span class="number">32</span> ): <span class="comment"># intermediate range, no correction</span></span><br><span class="line"> DV = DV_est</span><br><span class="line"><span class="keyword">if</span> DV_est > ( <span class="number">1</span>/<span class="number">30</span> * <span class="number">2</span>^<span class="number">32</span> ): <span class="comment"># large range correction</span></span><br><span class="line"> DV = -<span class="number">2</span>^<span class="number">32</span> * log( <span class="number">1</span> - DV_est/<span class="number">2</span>^<span class="number">32</span>)</span><br></pre></td></tr></table></figure><h2 id="通常使用"><a href="#通常使用" class="headerlink" title="通常使用"></a>通常使用</h2><ul><li>统计注册 IP 数</li><li>统计每日访问 IP 数</li><li>统计页面实时 UV 数</li><li>统计在线用户数</li><li>统计用户每天搜索不同词条的个数</li></ul>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 算法 </tag>
</tags>
</entry>
<entry>
<title>代理模式</title>
<link href="/2022/12/12/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/"/>
<url>/2022/12/12/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/</url>
<content type="html"><![CDATA[<h1 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h1><p>参考文章:<a href="https://javaguide.cn/java/basis/proxy.html">Java 代理模式详解 | JavaGuide(Java面试+学习指南)</a></p><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>代理模式:为<code>目标对象</code>提供一个<code>代理对象</code>,<strong>由<code>代理对象</code>控制对<code>目标对象</code>的访问</strong>,在不修改<code>目标对象</code>的前提下,提供额外的功能操作,扩展<code>目标对象</code>的功能。</p><h2 id="静态代理"><a href="#静态代理" class="headerlink" title="静态代理"></a>静态代理</h2><p>静态代理中,我们对<code>目标对象</code>的每个方法的增强都需要手动完成。</p><p>从 JVM 层面来说, <strong>静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件</strong>。</p><h3 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h3><p>静态代理实现步骤:</p><ol><li>定义接口及其实现类(<code>目标对象</code>);</li><li>创建代理类并实现该接口(<code>代理对象</code>);</li><li>将<code>目标对象</code>注入到<code>代理对象</code>中,在<code>代理对象</code>中的方法中调用<code>目标对象</code>中对应的方法;</li><li>使用<code>代理对象</code>中的方法;</li></ol><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><ol><li><p>定义短信发送接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SmsService</span> </span>{</span><br><span class="line"> <span class="function">String <span class="title">sendMsg</span><span class="params">(String message)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>实现短信发送接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SmsServiceImpl</span> <span class="keyword">implements</span> <span class="title">SmsService</span> </span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">sendMsg</span><span class="params">(String message)</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"send message --> "</span> + message);</span><br><span class="line"> <span class="keyword">return</span> message;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>创建代理类并实现短信发送接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SmsProxy</span> <span class="keyword">implements</span> <span class="title">SmsService</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> SmsService smsService;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">SmsProxy</span><span class="params">(SmsService smsService)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.smsService = smsService;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">sendMsg</span><span class="params">(String message)</span> </span>{</span><br><span class="line"> <span class="comment">// 调用方法之前</span></span><br><span class="line"> System.out.println(<span class="string">"before method sendMsg()"</span>);</span><br><span class="line"> <span class="comment">// 执行目标对象所对应的方法</span></span><br><span class="line"> smsService.sendMsg(message);</span><br><span class="line"> <span class="comment">// 调用方法之后</span></span><br><span class="line"> System.out.println(<span class="string">"after method sendMsg()"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>实际使用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span 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>{</span><br><span class="line"> <span class="comment">// 创建目标对象</span></span><br><span class="line"> SmsService smsService = <span class="keyword">new</span> SmsServiceImpl();</span><br><span class="line"> <span class="comment">// 创建代理对象</span></span><br><span class="line"> SmsProxy smsProxy = <span class="keyword">new</span> SmsProxy(smsService);</span><br><span class="line"> <span class="comment">// 使用代理对象的方法</span></span><br><span class="line"> smsProxy.sendMsg(<span class="string">"pigwantacat"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制台打印:</span></span><br><span class="line"><span class="comment">// before method sendMsg()</span></span><br><span class="line"><span class="comment">// send message --> pigwantacat</span></span><br><span class="line"><span class="comment">// after method sendMsg()</span></span><br></pre></td></tr></table></figure></li></ol><h2 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a>动态代理</h2><p>动态代理中,我们不需要针对每个目标类都单独创建一个代理类,也不需要代理类实现对应的接口。</p><p>从 JVM 角度来说,<strong>动态代理是在运行时动态生成类字节码,并加载到 JVM 中的</strong>。</p><p>就 Java 来说,动态代理的实现方式有很多种,比如 <strong>JDK 动态代理</strong>、<strong>CGLIB 动态代理</strong>等等。</p><h3 id="JDK动态代理"><a href="#JDK动态代理" class="headerlink" title="JDK动态代理"></a>JDK动态代理</h3><p><strong>在 Java 动态代理机制中 <code>Proxy</code> 类和<code>InvocationHandler</code> 接口是核心。</strong></p><p><code>Proxy</code> 类中使用频率最高的方法是:<code>newProxyInstance()</code> ,这个方法主要用来生成一个<code>代理对象</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">newProxyInstance</span><span class="params">(ClassLoader loader,</span></span></span><br><span class="line"><span class="params"><span class="function"> Class<?>[] interfaces,</span></span></span><br><span class="line"><span class="params"><span class="function"> InvocationHandler h)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> IllegalArgumentException</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> ......</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>newProxyInstance</code>方法中的参数解释:</p><ol><li><strong><code>loader</code></strong> :类加载器,用于加载<code>代理对象</code>。</li><li><strong><code>interfaces</code></strong> : 被代理类实现的接口;</li><li><strong><code>h</code></strong> : 实现了 <code>InvocationHandler</code> 接口的对象;</li></ol><p>要实现动态代理的话,必须实现<code>InvocationHandler</code> 来自定义处理逻辑。 当<code>代理对象</code>调用方法时,该方法的调用就会被转发到实现<code>InvocationHandler</code> 接口类的 <code>invoke</code> 方法中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">InvocationHandler</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 代理对象调用方法时实际会被转发到这个方法中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> Throwable</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>invoke()</code> 方法中的参数解释:</p><ol><li><strong><code>proxy</code></strong> :动态生成的代理类</li><li><strong><code>method</code></strong> : <code>目标对象</code>所对应的方法</li><li><strong><code>args</code></strong> : 当前 method 方法的参数</li></ol><p>也就是说:<strong>你通过<code>Proxy</code> 类的 <code>newProxyInstance()</code> 创建的<code>代理对象</code>在调用方法的时候,实际会调用到实现<code>InvocationHandler</code> 接口的类的 <code>invoke()</code>方法。</strong></p><h4 id="步骤-1"><a href="#步骤-1" class="headerlink" title="步骤"></a>步骤</h4><ol><li>定义一个接口及其实现类(<code>目标对象</code>);</li><li>自定义 <code>InvocationHandler</code> 并重写<code>invoke</code>方法,在 <code>invoke</code> 方法中调用<code>目标对象</code>的方法;</li><li>使用 <code>Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)</code> 方法创建<code>代理对象</code>;</li><li>使用<code>代理对象</code>中的方法;</li></ol><h4 id="示例-1"><a href="#示例-1" class="headerlink" title="示例"></a>示例</h4><ol><li><p>定义并实现短信发送接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SmsService</span> </span>{</span><br><span class="line"> <span class="function">String <span class="title">sendMsg</span><span class="params">(String message)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>实现短信发送接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SmsServiceImpl</span> <span class="keyword">implements</span> <span class="title">SmsService</span> </span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">sendMsg</span><span class="params">(String message)</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"send message --> "</span> + message);</span><br><span class="line"> <span class="keyword">return</span> message;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>实现JDK动态代理类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyInvocationHandler</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 目标对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Object target;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MyInvocationHandler</span><span class="params">(Object target)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.target = target;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">// 调用方法之前</span></span><br><span class="line"> System.out.println(<span class="string">"before method "</span> + method.getName());</span><br><span class="line"> <span class="comment">// 执行目标对象所对应的方法</span></span><br><span class="line"> Object result = method.invoke(target, args);</span><br><span class="line"> <span class="comment">// 调用方法之后</span></span><br><span class="line"> System.out.println(<span class="string">"after method "</span> + method.getName());</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>获取代理对象的工厂类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JdkProxyFactory</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">getProxy</span><span class="params">(Object target)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> Proxy.newProxyInstance(</span><br><span class="line"> <span class="comment">// 获取目标对象的类加载器</span></span><br><span class="line"> target.getClass().getClassLoader(),</span><br><span class="line"> <span class="comment">// 获取目标对象实现的接口集合</span></span><br><span class="line"> target.getClass().getInterfaces(),</span><br><span class="line"> <span class="comment">// 实现InvocationHandler接口的对象</span></span><br><span class="line"> <span class="keyword">new</span> MyInvocationHandler(target)</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>实际使用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span 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>{</span><br><span class="line"> <span class="comment">// 创建目标对象</span></span><br><span class="line"> SmsService smsService = <span class="keyword">new</span> SmsServiceImpl();</span><br><span class="line"> <span class="comment">// 创建代理对象</span></span><br><span class="line"> SmsService proxySmsService = (SmsService)JdkProxyFactory.getProxy(smsService);</span><br><span class="line"> <span class="comment">// 使用代理对象的方法</span></span><br><span class="line"> proxySmsService.sendMsg(<span class="string">"pigwantacat"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制台打印:</span></span><br><span class="line"><span class="comment">// before method sendMsg()</span></span><br><span class="line"><span class="comment">// send message --> pigwantacat</span></span><br><span class="line"><span class="comment">// after method sendMsg()</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="CGLIB动态代理"><a href="#CGLIB动态代理" class="headerlink" title="CGLIB动态代理"></a>CGLIB动态代理</h3><p><strong>JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类</strong>。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。</p><p><a href="https://github.com/cglib/cglib">CGLIBopen in new window</a>(<em>Code Generation Library</em>)是一个基于<a href="http://www.baeldung.com/java-asm">ASMopen in new window</a>的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了<a href="https://github.com/cglib/cglib">CGLIBopen in new window</a>, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。</p><p><strong>在 CGLIB 动态代理机制中 <code>MethodInterceptor</code> 接口和 <code>Enhancer</code> 类是核心。</strong></p><p>你需要自定义 <code>MethodInterceptor</code> 并重写 <code>intercept</code> 方法,<code>intercept</code> 用于拦截增强被代理类的方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MethodInterceptor</span></span></span><br><span class="line"><span class="class"><span class="keyword">extends</span> <span class="title">Callback</span></span>{</span><br><span class="line"> <span class="comment">// 拦截被代理类中的方法</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy)</span> <span class="keyword">throws</span> Throwable</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li><strong>obj</strong> : 被代理的对象(需要增强的对象)</li><li><strong>method</strong> : 被拦截的方法(需要增强的方法)</li><li><strong>args</strong> : 方法入参</li><li><strong>proxy</strong> : 用于调用原始方法</li></ol><p>你可以通过 <code>Enhancer</code>类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 <code>MethodInterceptor</code> 中的 <code>intercept</code> 方法。</p><h4 id="步骤-2"><a href="#步骤-2" class="headerlink" title="步骤"></a>步骤</h4><ol><li>定义一个类;</li><li>自定义 <code>MethodInterceptor</code> 并重写 <code>intercept</code> 方法,<code>intercept</code> 用于拦截增强被代理类的方法,和 JDK 动态代理中的 <code>invoke</code> 方法类似;</li><li>通过 <code>Enhancer</code> 类的 <code>create()</code>创建代理类;</li></ol><h4 id="示例-2"><a href="#示例-2" class="headerlink" title="示例"></a>示例</h4><p>不同于 JDK 动态代理不需要额外的依赖。<a href="https://github.com/cglib/cglib">CGLIBopen in new window</a>(<em>Code Generation Library</em>) 实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。</p><ol><li><p>引入CGLIB依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>cglib<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>cglib<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>3.3.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></li><li><p>实现短信发送接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AliSmsService</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">sendMsg</span><span class="params">(String message)</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"send message --> "</span> + message);</span><br><span class="line"> <span class="keyword">return</span> message;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>自定义 <code>MethodInterceptor</code>(方法拦截器)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义MethodInterceptor</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyMethodInterceptor</span> <span class="keyword">implements</span> <span class="title">MethodInterceptor</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> o 被代理的对象(需要增强的对象)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> method 被拦截的方法(需要增强的方法)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> args 方法入参</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> methodProxy 用于调用原始方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Object o, Method method, Object[] args, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"> <span class="comment">//调用方法之前</span></span><br><span class="line"> System.out.println(<span class="string">"before method "</span> + method.getName());</span><br><span class="line"> <span class="comment">// 执行目标对象所对应的方法</span></span><br><span class="line"> Object object = methodProxy.invokeSuper(o, args);</span><br><span class="line"> <span class="comment">//调用方法之后</span></span><br><span class="line"> System.out.println(<span class="string">"after method "</span> + method.getName());</span><br><span class="line"> <span class="keyword">return</span> object;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>获取代理对象的工厂类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CglibProxyFactory</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">getProxy</span><span class="params">(Class<?> clazz)</span> </span>{</span><br><span class="line"> <span class="comment">// 创建动态代理增强类</span></span><br><span class="line"> Enhancer enhancer = <span class="keyword">new</span> Enhancer();</span><br><span class="line"> <span class="comment">// 设置类加载器</span></span><br><span class="line"> enhancer.setClassLoader(clazz.getClassLoader());</span><br><span class="line"> <span class="comment">// 设置被代理类</span></span><br><span class="line"> enhancer.setSuperclass(clazz);</span><br><span class="line"> <span class="comment">// 设置方法拦截器</span></span><br><span class="line"> enhancer.setCallback(<span class="keyword">new</span> MyMethodInterceptor());</span><br><span class="line"> <span class="comment">// 创建代理类</span></span><br><span class="line"> <span class="keyword">return</span> enhancer.create();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>实际使用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span 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>{</span><br><span class="line"> AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);</span><br><span class="line">aliSmsService.sendMsg(<span class="string">"pigwantacat"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制台打印:</span></span><br><span class="line"><span class="comment">// before method sendMsg()</span></span><br><span class="line"><span class="comment">// send message --> pigwantacat</span></span><br><span class="line"><span class="comment">// after method sendMsg()</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="JDK-动态代理和-CGLIB-动态代理对比"><a href="#JDK-动态代理和-CGLIB-动态代理对比" class="headerlink" title="JDK 动态代理和 CGLIB 动态代理对比"></a>JDK 动态代理和 CGLIB 动态代理对比</h3><ol><li><strong>JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。</strong> 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。</li><li>就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。</li></ol><h2 id="静态代理和动态代理的对比"><a href="#静态代理和动态代理的对比" class="headerlink" title="静态代理和动态代理的对比"></a>静态代理和动态代理的对比</h2><ol><li><strong>灵活性</strong> :动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!</li><li><strong>JVM 层面</strong> :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。</li></ol>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 设计模式 </tag>
</tags>
</entry>
<entry>
<title>函数式接口</title>
<link href="/2022/12/08/%E5%87%BD%E6%95%B0%E5%BC%8F%E6%8E%A5%E5%8F%A3/"/>
<url>/2022/12/08/%E5%87%BD%E6%95%B0%E5%BC%8F%E6%8E%A5%E5%8F%A3/</url>
<content type="html"><![CDATA[<h1 id="函数式接口"><a href="#函数式接口" class="headerlink" title="函数式接口"></a>函数式接口</h1><p>参考文章:<a href="https://zhuanlan.zhihu.com/p/339804728">深度解析之Java8-函数式接口 - 知乎 (zhihu.com)</a>,<a href="https://mp.weixin.qq.com/s/ud5TckFLXWrVpilmobynhQ">微信公众平台 (qq.com)</a></p><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>函数式接口:有且只有一个抽象方法的接口。</p><h2 id="FunctionalInterface注解"><a href="#FunctionalInterface注解" class="headerlink" title="@FunctionalInterface注解"></a>@FunctionalInterface注解</h2><p>Java 8中为函数式接口引入了一个新的注解:<code>@FunctionalInterface</code> 。该注解可用于一个接口的定义上,在接口上使用该注解,编译器将会强制检查该接口是否<code>有且仅有一个抽象方法</code>。但是该注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。</p><h2 id="原生函数式接口"><a href="#原生函数式接口" class="headerlink" title="原生函数式接口"></a>原生函数式接口</h2><h3 id="Function-函数型接口"><a href="#Function-函数型接口" class="headerlink" title="Function: 函数型接口"></a>Function: 函数型接口</h3><p><code>Function</code>接口是一个转换类型的接口,用来根据一个类型的数据得到另一个类型的数据,T称为前置条件,R称为后置条件(<strong>即有入参,有返回,其中T是入参,R是返回</strong>)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Function</span><<span class="title">T</span>, <span class="title">R</span>> </span>{</span><br><span class="line"> <span class="function">R <span class="title">apply</span><span class="params">(T t)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Predicate-断言型接口"><a href="#Predicate-断言型接口" class="headerlink" title="Predicate: 断言型接口"></a>Predicate: 断言型接口</h3><p><code>Predicate</code>接口是一个判断型接口,可以看做<code>Function</code>接口的特例(<strong>即有入参,有返回,凡是返回的类型固定为boolean</strong>)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Predicate</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">test</span><span class="params">(T t)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Consumer-消费型接口"><a href="#Consumer-消费型接口" class="headerlink" title="Consumer: 消费型接口"></a>Consumer: 消费型接口</h3><p><code>Consumer</code>接口是一个消费型接口,主要负责消费数据(<strong>即有入参,无返回</strong>)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Consumer</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">accept</span><span class="params">(T t)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Supplier-供给型接口"><a href="#Supplier-供给型接口" class="headerlink" title="Supplier: 供给型接口"></a>Supplier: 供给型接口</h3><p><code>Supplier</code>接口是一个获取型接口,与<code>Consumer</code>接口相反,主要负责产生数据(即无入参,有返回)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Supplier</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="function">T <span class="title">get</span><span class="params">()</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span 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>{</span><br><span class="line"> <span class="comment">/** 函数型接口 */</span></span><br><span class="line"> Function<String,Integer> function = s->{</span><br><span class="line"> <span class="keyword">return</span> s.length();</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">/** 断言型接口 */</span></span><br><span class="line"> Predicate<String> predicate = s->{</span><br><span class="line"> <span class="keyword">return</span> s.length() <= Byte.MAX_VALUE;</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">/** 消费型接口 */</span></span><br><span class="line"> Consumer<String> consumer = s -> {</span><br><span class="line"> System.out.println(s);</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">/** 供给型接口 */</span></span><br><span class="line"> Supplier<String> supplier = ()->{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"PigwantAcat"</span>;</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> System.out.println(function.apply(<span class="string">"PigwantAcat"</span>));</span><br><span class="line"> System.out.println(predicate.test(<span class="string">"PigwantAcat"</span>));</span><br><span class="line"> consumer.accept(<span class="string">"PigwantAcat"</span>);</span><br><span class="line"> System.out.println(supplier.get());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制台输出如下:</span></span><br><span class="line"><span class="comment">// 11</span></span><br><span class="line"><span class="comment">// true</span></span><br><span class="line"><span class="comment">// PigwantAcat</span></span><br><span class="line"><span class="comment">// PigwantAcat</span></span><br></pre></td></tr></table></figure><h2 id="默认方法和静态方法"><a href="#默认方法和静态方法" class="headerlink" title="默认方法和静态方法"></a>默认方法和静态方法</h2><p>Java 8中允许接口有静态方法和默认方法。</p><h3 id="自定义的函数式接口"><a href="#自定义的函数式接口" class="headerlink" title="自定义的函数式接口"></a>自定义的函数式接口</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 函数式接口中只能有一个抽象方法,可以有默认方法和静态方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">MyFunctionInterface</span></span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 可以有常量,默认是【pubic static final】</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> String NAME=<span class="string">"测试"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 测试函数式接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">myTest</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 测试默认方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">default</span> <span class="keyword">void</span> <span class="title">myDefaultTest</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"可以有默认方法"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 测试静态方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">myStaticTest</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"可以有静态方法"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取name的值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> java.lang.String</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">static</span> String <span class="title">getName</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> NAME;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span 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>{</span><br><span class="line"><span class="comment">// MyFunctionInterface myFunctionInterface = new MyFunctionInterface() {</span></span><br><span class="line"><span class="comment">// @Override</span></span><br><span class="line"><span class="comment">// public void myTest() {</span></span><br><span class="line"><span class="comment">// System.out.println("测试:函数式接口 重写");</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"><span class="comment">// };</span></span><br><span class="line"> MyFunctionInterface mfi = ()->{</span><br><span class="line"> System.out.println(<span class="string">"测试:函数式接口 lambda简化"</span>);</span><br><span class="line"> };</span><br><span class="line"> mfi.myTest();</span><br><span class="line"> mfi.myDefaultTest();</span><br><span class="line"> </span><br><span class="line"> MyFunctionInterface.myStaticTest();</span><br><span class="line"> System.out.println(MyFunctionInterface.NAME);</span><br><span class="line"> System.out.println(MyFunctionInterface.getName());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制台输出如下:</span></span><br><span class="line"><span class="comment">// 测试:函数式接口 lambda简化</span></span><br><span class="line"><span class="comment">// 可以有默认方法</span></span><br><span class="line"><span class="comment">// 可以有静态方法</span></span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="comment">// 测试</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> stream </tag>
<tag> 函数式接口 </tag>
</tags>
</entry>
<entry>
<title>布隆过滤器</title>
<link href="/2022/10/25/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/"/>
<url>/2022/10/25/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/</url>
<content type="html"><![CDATA[<h1 id="布隆过滤器"><a href="#布隆过滤器" class="headerlink" title="布隆过滤器"></a>布隆过滤器</h1><p>参考文章:</p><ol><li><a href="https://developer.aliyun.com/article/773205#slide-5">布隆过滤器,这一篇给你讲的明明白白-阿里云开发者社区 (aliyun.com)</a></li><li><a href="https://hogwartsrico.github.io/2020/06/08/BloomFilter-HyperLogLog-BitMap/index.html">布隆过滤器,位图,HyperLogLog | Rico’s Blog (hogwartsrico.github.io)</a></li><li><a href="https://blog.csdn.net/tick_tock97/article/details/78688159">海量数据判重——布隆过滤器(Bloom filter)与Bitmap对比_tick_tokc97的博客-CSDN博客</a></li><li><a href="https://www.jianshu.com/p/3dd0bbf6380c">数据结构与算法必知— Bitmap位图与布隆过滤器 - 简书 (jianshu.com)</a></li></ol><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>布隆过滤器(Bloom filter)是一个高空间利用率的概率性数据结构,由Burton Bloom于1970年提出。实际是基于位图来实现的一种数据存储结构,<strong>用来判断某个元素(key)是否在某个集合中。</strong>和一般的位图不同的是,这个算法无需存储key的值,对于每个key,只需要k个比特位,每个存储一个标志,用来判断key是否在集合中。</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>布隆过滤器<strong>对一个对象使用多个哈希函数。如果我们使用了 k 个哈希函数,就会得到 k 个哈希值,也就是 k 个下标,我们会把数组中对应下标位置的值都置为 1</strong>。布隆过滤器和位图最大的区别就在于,<strong>我们不再使用一位来表示一个对象,而是使用 k 位来表示一个对象。这样两个对象的 k 位都相同的概率就会大大降低,不仅能够解决单哈希容易造成冲突的问题,还能有效减少内存空间的使用。</strong></p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/3.png" alt="布隆过滤器"></p><p>优缺点:</p><ul><li>优点:不需要存储key,节省空间</li><li>缺点:<ul><li>假阳性,存在一定的误判</li><li>添加过的元素无法删除</li></ul></li></ul><h2 id="误判"><a href="#误判" class="headerlink" title="误判"></a>误判</h2><p>布隆过滤器的误判是指:多个对象经过哈希之后将某个相同位置的bit位 置为1,这样无法判断是哪个对象产生的,因此误判的根源在于相同的 bit 位被多次映射且置 为1。</p><p>这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素。</p><p>特点:<strong>一个对象如果判断结果为存在的时候该对象不一定存在,但是判断结果为不存在的时候则一定不存在</strong>。</p><h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><ul><li>防止缓存击穿</li><li>URL去重</li><li>垃圾邮件识别</li><li>大集合中的重复元素</li><li>……</li></ul><h2 id="与Bitmap的区别"><a href="#与Bitmap的区别" class="headerlink" title="与Bitmap的区别"></a>与Bitmap的区别</h2><p>假设我们有1千万个(不重复、未排序)的整数需要存储,每个整数的大小范围是1到1亿,那么我们的Bitmap就需要需要1亿个连续长度的bit</p><p>空间不随集合内元素个数的增加而增加,比如上面这个例子不管数量是1千万还是2千万,都不会影响空间大小</p><p>但是不足之处也同样明显:空间随集合内边界元素的扩大而扩大,比如上面这个例子当范围改变时,集合大小就需要跟着改变</p><p>Bitmap用于处理非数字类型的时候,对内存空间的占用较大,效果并不理想,使用布隆过滤器更为合理。</p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 过滤器 </tag>
</tags>
</entry>
<entry>
<title>try、catch、finally执行顺序</title>
<link href="/2022/10/22/try%E3%80%81catch%E3%80%81finally%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/"/>
<url>/2022/10/22/try%E3%80%81catch%E3%80%81finally%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/</url>
<content type="html"><![CDATA[<h1 id="try、catch、finally执行顺序"><a href="#try、catch、finally执行顺序" class="headerlink" title="try、catch、finally执行顺序"></a>try、catch、finally执行顺序</h1><h2 id="try中有return"><a href="#try中有return" class="headerlink" title="try中有return"></a>try中有return</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span 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 class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"返回结果:"</span>+test());</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> try "</span>);</span><br><span class="line"> num = <span class="number">1</span>;</span><br><span class="line"> System.out.println(<span class="string">"try方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> System.out.println(<span class="string">"进入--> catch "</span>);</span><br><span class="line"> num = <span class="number">2</span>;</span><br><span class="line"> System.out.println(<span class="string">"catch方法--> num="</span> + num);</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> finally "</span>);</span><br><span class="line"> num = <span class="number">3</span>;</span><br><span class="line"> System.out.println(<span class="string">"finally方法--> num="</span> + num);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 输出结果:</span></span><br><span class="line"><span class="comment">// 进入--> try </span></span><br><span class="line"><span class="comment">// try方法--> num=1</span></span><br><span class="line"><span class="comment">// 进入--> finally </span></span><br><span class="line"><span class="comment">// finally方法--> num=3</span></span><br><span class="line"><span class="comment">// 返回结果:1</span></span><br></pre></td></tr></table></figure><h2 id="catch中有return"><a href="#catch中有return" class="headerlink" title="catch中有return"></a>catch中有return</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span 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 class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"返回结果:"</span>+test());</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> try "</span>);</span><br><span class="line"> <span class="comment">// 产生异常</span></span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">1</span>/<span class="number">0</span>;</span><br><span class="line"> num = <span class="number">1</span>;</span><br><span class="line"> System.out.println(<span class="string">"try方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> System.out.println(<span class="string">"进入--> catch "</span>);</span><br><span class="line"> num = <span class="number">2</span>;</span><br><span class="line"> System.out.println(<span class="string">"catch方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> finally "</span>);</span><br><span class="line"> num = <span class="number">3</span>;</span><br><span class="line"> System.out.println(<span class="string">"finally方法--> num="</span> + num);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 输出结果:</span></span><br><span class="line"><span class="comment">// 进入--> try </span></span><br><span class="line"><span class="comment">// 进入--> catch </span></span><br><span class="line"><span class="comment">// catch方法--> num=2</span></span><br><span class="line"><span class="comment">// 进入--> finally </span></span><br><span class="line"><span class="comment">// finally方法--> num=3</span></span><br><span class="line"><span class="comment">// 返回结果:2</span></span><br></pre></td></tr></table></figure><h2 id="finally中有return"><a href="#finally中有return" class="headerlink" title="finally中有return"></a>finally中有return</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span 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 class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"返回结果:"</span>+test());</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> try "</span>);</span><br><span class="line"> num = <span class="number">1</span>;</span><br><span class="line"> System.out.println(<span class="string">"try方法--> num="</span> + num);</span><br><span class="line"> <span class="comment">// 产生异常</span></span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">1</span>/<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> System.out.println(<span class="string">"进入--> catch "</span>);</span><br><span class="line"> num = <span class="number">2</span>;</span><br><span class="line"> System.out.println(<span class="string">"catch方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> finally "</span>);</span><br><span class="line"> num = <span class="number">3</span>;</span><br><span class="line"> System.out.println(<span class="string">"finally方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 输出结果:</span></span><br><span class="line"><span class="comment">// 进入--> try </span></span><br><span class="line"><span class="comment">// try方法--> num=1</span></span><br><span class="line"><span class="comment">// 进入--> catch </span></span><br><span class="line"><span class="comment">// catch方法--> num=2</span></span><br><span class="line"><span class="comment">// 进入--> finally </span></span><br><span class="line"><span class="comment">// finally方法--> num=3</span></span><br><span class="line"><span class="comment">// 返回结果:3</span></span><br></pre></td></tr></table></figure><h2 id="return的值为对象"><a href="#return的值为对象" class="headerlink" title="return的值为对象"></a>return的值为对象</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span 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 class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"返回结果:"</span>+ArrayUtil.toString(test()));</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> List<String> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> List<String> list = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> try "</span>);</span><br><span class="line"> list.add(<span class="string">"try"</span>);</span><br><span class="line"> System.out.println(<span class="string">"try方法--> list="</span> + ArrayUtil.toString(list));</span><br><span class="line"> <span class="comment">// 产生异常</span></span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">1</span>/<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> System.out.println(<span class="string">"进入--> catch "</span>);</span><br><span class="line"> list.add(<span class="string">"catch"</span>);</span><br><span class="line"> System.out.println(<span class="string">"catch方法--> list="</span> + ArrayUtil.toString(list));</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> finally "</span>);</span><br><span class="line"> list.add(<span class="string">"finally"</span>);</span><br><span class="line"> System.out.println(<span class="string">"finally方法--> list="</span> + ArrayUtil.toString(list));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 输出结果:</span></span><br><span class="line"><span class="comment">// 进入--> try </span></span><br><span class="line"><span class="comment">// try方法--> list=[try]</span></span><br><span class="line"><span class="comment">// 进入--> catch </span></span><br><span class="line"><span class="comment">// catch方法--> list=[try, catch]</span></span><br><span class="line"><span class="comment">// 进入--> finally </span></span><br><span class="line"><span class="comment">// finally方法--> list=[try, catch, finally]</span></span><br><span class="line"><span class="comment">// 返回结果:[try, catch, finally]</span></span><br></pre></td></tr></table></figure><h2 id="程序退出或其他导致程序中断"><a href="#程序退出或其他导致程序中断" class="headerlink" title="程序退出或其他导致程序中断"></a>程序退出或其他导致程序中断</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span 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 class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"返回结果:"</span>+test());</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">test</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> try "</span>);</span><br><span class="line"> <span class="comment">// 退出程序</span></span><br><span class="line"> System.exit(<span class="number">0</span>);</span><br><span class="line"> num = <span class="number">1</span>;</span><br><span class="line"> System.out.println(<span class="string">"try方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> System.out.println(<span class="string">"进入--> catch "</span>);</span><br><span class="line"> num = <span class="number">2</span>;</span><br><span class="line"> System.out.println(<span class="string">"catch方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> System.out.println(<span class="string">"进入--> finally "</span>);</span><br><span class="line"> num = <span class="number">3</span>;</span><br><span class="line"> System.out.println(<span class="string">"finally方法--> num="</span> + num);</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 输出结果:</span></span><br><span class="line"><span class="comment">// 进入--> try </span></span><br></pre></td></tr></table></figure><h2 id="总结:"><a href="#总结:" class="headerlink" title="总结:"></a>总结:</h2><ol><li>执行顺序:try –> catch(如果有异常)–> finally –> finally中的return –> try/catch中的return</li><li>finally中有return时,会直接在finally中退出,导致try、catch中的return失效</li><li>finally不一定会被执行,比如手动调用System.exit(0)或栈溢出、内存溢出等其他导致程序中断的情况</li><li>需要注意return返回值的类型,是否会受到finally中代码的影响<ol><li>如果是基本数据类型:当try中带有return时,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息</li><li>如果是对象:return中保存的是对象的引用地址,当在finally中改变对象的属性时,return返回的对象也会跟着改变</li></ol></li></ol>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 异常 </tag>
</tags>
</entry>
<entry>
<title>生成随机字符串</title>
<link href="/2022/05/12/%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/"/>
<url>/2022/05/12/%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/</url>
<content type="html"><![CDATA[<h1 id="生成随机字符串"><a href="#生成随机字符串" class="headerlink" title="生成随机字符串"></a>生成随机字符串</h1><h2 id="随机数生成工具"><a href="#随机数生成工具" class="headerlink" title="随机数生成工具"></a>随机数生成工具</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Random;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 伪随机数生成类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Random RANDOM = <span class="keyword">new</span> Random();</span><br></pre></td></tr></table></figure><h2 id="获取随机数"><a href="#获取随机数" class="headerlink" title="获取随机数"></a>获取随机数</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取指定范围内的随机数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value 随机数最大值+1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> [0,value)中的整数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">getRandomNumber</span><span class="params">(<span class="keyword">int</span> value)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> RANDOM.nextInt(value);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="获取随机字符串"><a href="#获取随机字符串" class="headerlink" title="获取随机字符串"></a>获取随机字符串</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 从str中获取随机字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> size 字符串长度</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> str 字符来源集合</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 随机字符集合</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">getRandomString</span><span class="params">(<span class="keyword">int</span> size,String str)</span></span>{</span><br><span class="line"> <span class="keyword">if</span> (size <= <span class="number">0</span> || str == <span class="keyword">null</span> || str.length() <= <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"> StringBuilder sb = <span class="keyword">new</span> StringBuilder();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < size; i++) {</span><br><span class="line"> sb.append(</span><br><span class="line"> str.charAt(</span><br><span class="line"> getRandomNumber(str.length())));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sb.toString();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="交换字符位置"><a href="#交换字符位置" class="headerlink" title="交换字符位置"></a>交换字符位置</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 交换字符位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> chars 字符数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> i 字符1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> j 字符2</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(<span class="keyword">char</span>[] chars,<span class="keyword">int</span> i,<span class="keyword">int</span> j)</span></span>{</span><br><span class="line"> <span class="keyword">if</span> (i == j){</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">char</span> ch = chars[i];</span><br><span class="line"> chars[i] = chars[j];</span><br><span class="line"> chars[j] = ch;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="乱序"><a href="#乱序" class="headerlink" title="乱序"></a>乱序</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 打乱字符串顺序</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> str 字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 乱序后的字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">shuffleString</span><span class="params">(String str)</span></span>{</span><br><span class="line"> <span class="keyword">char</span>[] chars = str.toCharArray();</span><br><span class="line"> <span class="comment">// 参考自java.util.Collections.shuffle()</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = chars.length; i > <span class="number">1</span>; i--) {</span><br><span class="line"> swap(chars, i-<span class="number">1</span>, RANDOM.nextInt(i));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> String.valueOf(chars);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> string </tag>
</tags>
</entry>
<entry>
<title>重构字符串,使得两相邻的字符不同</title>
<link href="/2022/03/08/%E9%87%8D%E6%9E%84%E5%AD%97%E7%AC%A6%E4%B8%B2%EF%BC%8C%E4%BD%BF%E5%BE%97%E4%B8%A4%E7%9B%B8%E9%82%BB%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%8D%E5%90%8C/"/>
<url>/2022/03/08/%E9%87%8D%E6%9E%84%E5%AD%97%E7%AC%A6%E4%B8%B2%EF%BC%8C%E4%BD%BF%E5%BE%97%E4%B8%A4%E7%9B%B8%E9%82%BB%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%8D%E5%90%8C/</url>
<content type="html"><![CDATA[<h1 id="重构字符串,使得两相邻的字符不同"><a href="#重构字符串,使得两相邻的字符不同" class="headerlink" title="重构字符串,使得两相邻的字符不同"></a>重构字符串,使得两相邻的字符不同</h1><p>题目链接:<a href="https://leetcode-cn.com/problems/reorganize-string/">767. 重构字符串 - 力扣(LeetCode) (leetcode-cn.com)</a></p><p>参考文章:<a href="https://blog.csdn.net/haut_ykc/article/details/104570022">(17条消息) JAVA程序设计:重构字符串(LeetCode:767)_信仰.的博客-CSDN博客</a></p><h2 id="思路一:排序"><a href="#思路一:排序" class="headerlink" title="思路一:排序"></a>思路一:排序</h2><p>我们按照字符出现的频率进行排序,这样相同的字符此时是连续出现的,我们可以每隔一个字符空位插入一个字符,但是有一种特殊情况是出现次数最多的字符可能出现了(n+1)/2次,此时有可能前两个字符会出现相同的情况,为了避免这种情况,我们要使出现次数最多的字母排在最后,因此我们选择从小到大排序,问题解决了</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">reorganizeString</span><span class="params">(String S)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> len=S.length();</span><br><span class="line"> <span class="keyword">int</span>[] nums=<span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">26</span>];</span><br><span class="line"> <span class="comment">// 例如S="aabbccc",则nums={200,200,300}</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">char</span> c : S.toCharArray()) {</span><br><span class="line"> nums[c-<span class="string">'a'</span>]+=<span class="number">100</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 例如S="aabbccc",则nums={200,201,302}</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i<<span class="number">26</span>;i++) {</span><br><span class="line"> nums[i]+=i;</span><br><span class="line"> }</span><br><span class="line"> Arrays.parallelSort(nums);</span><br><span class="line"> <span class="keyword">int</span> t=<span class="number">1</span>;</span><br><span class="line"> <span class="keyword">char</span>[] ans=<span class="keyword">new</span> <span class="keyword">char</span>[len];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> num : nums) {</span><br><span class="line"> <span class="comment">// 获取字符的数量</span></span><br><span class="line"> <span class="keyword">int</span> ct=num/<span class="number">100</span>;</span><br><span class="line"> <span class="comment">// 获取具体字符</span></span><br><span class="line"> <span class="keyword">char</span> ch=(<span class="keyword">char</span>)(<span class="string">'a'</span>+(num%<span class="number">100</span>));</span><br><span class="line"> <span class="comment">// 如果字符数超过一半,则不满足</span></span><br><span class="line"> <span class="keyword">if</span>(ct>(len+<span class="number">1</span>)/<span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 间隔插入字符</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i<ct;i++) {</span><br><span class="line"> <span class="keyword">if</span>(t>=len) {</span><br><span class="line"> t=<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> ans[t]=ch;</span><br><span class="line"> t+=<span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> String(ans);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="思路二:优先队列"><a href="#思路二:优先队列" class="headerlink" title="思路二:优先队列"></a>思路二:优先队列</h2><p>我们考虑每次插入的字符是出现次数最多的字符,可以这么做的原因在于,只有当一个字符出现的次数超过 (N+1) / 2 的情况下,这个问题才无解。只要初试情况下这个条件满足,每次都输出剩余出现次数最多的字符就可以将这个条件一直保持下去</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span></span>{</span><br><span class="line"> <span class="comment">// 字符数量</span></span><br><span class="line"> <span class="keyword">int</span> count;</span><br><span class="line"> <span class="comment">// 具体字符</span></span><br><span class="line"> <span class="keyword">char</span> letter;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">node</span><span class="params">(<span class="keyword">int</span> count,<span class="keyword">char</span> letter)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.count=count;</span><br><span class="line"> <span class="keyword">this</span>.letter=letter;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">reorganizeString</span><span class="params">(String S)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> len=S.length();</span><br><span class="line"> <span class="keyword">int</span>[] nums=<span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">26</span>];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">char</span> c : S.toCharArray()) {</span><br><span class="line"> nums[c-<span class="string">'a'</span>]++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 创建优先队列,按字符数量大小倒排,再按字符大小正排</span></span><br><span class="line"> PriorityQueue<node> q=<span class="keyword">new</span> PriorityQueue<node>(</span><br><span class="line"> (a,b)->a.count==b.count?a.letter-b.letter:b.count-a.count);</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i<<span class="number">26</span>;i++) {</span><br><span class="line"> <span class="keyword">if</span>(nums[i]==<span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果字符数超过一半,则不满足</span></span><br><span class="line"> <span class="keyword">if</span>(nums[i]>(len+<span class="number">1</span>)/<span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"> q.add(<span class="keyword">new</span> node(nums[i],(<span class="keyword">char</span>)(<span class="string">'a'</span>+i)));</span><br><span class="line"> }</span><br><span class="line"> StringBuilder ans=<span class="keyword">new</span> StringBuilder();</span><br><span class="line"> <span class="comment">// 两两添加字符</span></span><br><span class="line"> <span class="keyword">while</span>(q.size()>=<span class="number">2</span>) {</span><br><span class="line"> <span class="comment">// 获取优先级最高的两个字符</span></span><br><span class="line"> node a1=q.poll();</span><br><span class="line"> node a2=q.poll();</span><br><span class="line"> <span class="comment">// 依次排序</span></span><br><span class="line"> ans.append(a1.letter);</span><br><span class="line"> ans.append(a2.letter);</span><br><span class="line"> <span class="comment">// 使用后,数量减一再放回优先队列中</span></span><br><span class="line"> <span class="keyword">if</span>(--a1.count><span class="number">0</span>) {</span><br><span class="line"> q.add(a1);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(--a2.count><span class="number">0</span>) {</span><br><span class="line"> q.add(a2);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果队列中还存在字符</span></span><br><span class="line"> <span class="keyword">if</span>(q.size()><span class="number">0</span>) {</span><br><span class="line"> ans.append(q.poll().letter);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> String(ans);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
<tag> 队列 </tag>
</tags>
</entry>
<entry>
<title>类对象的生命周期</title>
<link href="/2022/02/17/%E7%B1%BB%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"/>
<url>/2022/02/17/%E7%B1%BB%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/</url>
<content type="html"><![CDATA[<h1 id="类对象的生命周期"><a href="#类对象的生命周期" class="headerlink" title="类对象的生命周期"></a>类对象的生命周期</h1><p>参考文章:<a href="https://javaguide.cn/java/jvm/class-loading-process/#">类加载过程详解 | JavaGuide</a></p><p><a href="http://www.iigrowing.cn/java_dui_xiang_tou_xiang_jie.html">http://www.iigrowing.cn/java_dui_xiang_tou_xiang_jie.html</a></p><h2 id="类的生命周期"><a href="#类的生命周期" class="headerlink" title="类的生命周期"></a>类的生命周期</h2><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/20220217185606.png" alt="类的生命周期"></p><h2 id="类的加载过程"><a href="#类的加载过程" class="headerlink" title="类的加载过程"></a>类的加载过程</h2><ul><li>编写相应的.java文件</li><li>通过javac编译为.class文件<ul><li>为编译期间可确定的常量赋值</li></ul></li><li>加载(双亲委派机制)<ol><li>通过全类名在字节码文件中获取定义此类的二进制字节流</li><li>通过二进制字节流加载到方法区中</li><li>在内存中生成相应的class对象</li></ol></li><li>验证<ol><li>验证文件格式</li><li>验证元数据</li><li>验证字节码</li><li>验证符号引用</li></ol></li><li>准备<ul><li>为类静态变量分配内存并赋初值(如public static int a)</li></ul></li><li>解析<ul><li>将符号引用转变为直接引用(比如在代码中调用了静态方法A(),此时将其替换成A()方法的实际内存地址)</li></ul></li><li>初始化<ul><li>为静态变量赋正确的值</li></ul></li><li>使用</li><li>卸载<ul><li>该类所有的实例都已经被回收</li><li>该类的类加载器的实例被回收(在 JVM 生命周期内,由 jvm 自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的(jdk 自带的 <code>BootstrapClassLoader</code>, <code>ExtClassLoader</code>, <code>AppClassLoader</code> 负责加载 jdk 提供的类,所以它们(类加载器的实例)肯定不会被回收。而我们自定义的类加载器的实例是可以被回收的,所以使用我们自定义加载器加载的类是可以被卸载掉的。)</li><li>该类没有在其他任何地方被引用(如在B类中引用A类的某个静态属性、方法)</li><li>虚拟机可以对满足上述 3 个条件的无用类进行回收,<strong>这里说的仅仅是“可以”</strong>,而并不是和对象一样不使用了就会必然被回收。</li></ul></li></ul><h2 id="对象的加载过程"><a href="#对象的加载过程" class="headerlink" title="对象的加载过程"></a>对象的加载过程</h2><ul><li>确保相应的类加载完成</li><li>在堆上分配内存并赋初值<ul><li>当类加载完成后,对象所需内存便可完全确定</li><li>内存分配方式:<ul><li>选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定</li><li>指针碰撞(内存规整)</li><li>空闲列表(内存不规整)</li></ul></li><li>内存分配并发问题<ul><li>CAS+失败重试</li><li>TLAB:为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配</li></ul></li></ul></li><li>设置对象头<ul><li>这个对象来自哪个类</li><li>对象自身的信息<ul><li>哈希码</li><li>GC年龄</li><li>偏向锁</li><li>……</li></ul></li></ul></li><li>初始化<ul><li>如果其父类还未初始化,则先触发该父类的初始化</li><li>顺序为父静、子静、父非静、父构造、子非静、子构造</li></ul></li><li>使用</li><li>回收<ul><li>没有被任何强引用指向</li><li>被软引用指向,但内存不足时</li><li>被弱引用指向,被GC时</li></ul></li></ul><h2 id="双亲委派机制"><a href="#双亲委派机制" class="headerlink" title="双亲委派机制"></a>双亲委派机制</h2><p>jdk 自带的 <code>BootstrapClassLoader</code>, <code>ExtClassLoader</code>, <code>AppClassLoader</code> 负责加载 jdk 提供的类,其为组合关系,并非父子类关系</p><ul><li>BootstrapClassLoader:负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,我们常用内置库 java.xxx.* 都在里面,比如 <code>java.util.</code>,<code>java.io.</code>,<code>java.nio.</code>,<code>java.lang.</code>等等</li><li>ExtensionClassLoader:负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中</li><li>AppClassLoader :它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录,我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的</li><li>自定义类加载器是AppClassLoader的子类</li></ul><p>向上验证,向下加载</p><p><a href="https://blog.csdn.net/weixin_39517868/article/details/111390001"> 双亲委派机制的原理和作用是什么?</a></p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>单调队列解决滑动窗口问题</title>
<link href="/2022/02/03/%E5%8D%95%E8%B0%83%E9%98%9F%E5%88%97%E8%A7%A3%E5%86%B3%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E9%97%AE%E9%A2%98/"/>
<url>/2022/02/03/%E5%8D%95%E8%B0%83%E9%98%9F%E5%88%97%E8%A7%A3%E5%86%B3%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<h1 id="单调队列解决滑动窗口问题"><a href="#单调队列解决滑动窗口问题" class="headerlink" title="单调队列解决滑动窗口问题"></a>单调队列解决滑动窗口问题</h1><p>力扣链接:<a href="https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/">剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(LeetCode) (leetcode-cn.com)</a></p><p><a href="https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof/">剑指 Offer 59 - II. 队列的最大值 - 力扣(LeetCode) (leetcode-cn.com)</a></p><h2 id="单调队列"><a href="#单调队列" class="headerlink" title="单调队列"></a>单调队列</h2><p>队列中的元素呈现一定的单调性,单调递增或单调递减</p><h2 id="滑动窗口问题"><a href="#滑动窗口问题" class="headerlink" title="滑动窗口问题"></a>滑动窗口问题</h2><p>指求解在指定范围内的最值问题,并且这个范围还会移动或改变</p><h2 id="滑动窗口的最大值"><a href="#滑动窗口的最大值" class="headerlink" title="滑动窗口的最大值"></a>滑动窗口的最大值</h2><p>滑动窗口的最大值:<a href="https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/">剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(LeetCode) (leetcode-cn.com)</a></p><h3 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h3><ul><li>构造一个单调递减的队列</li><li>获取当前队列的首元素即可获取当前窗口的最大值</li><li>当单调队列中的最大值在滑动窗口范围外,就弹出队首元素</li><li>当新增元素大于等于队尾元素时,就不断弹出队尾元素,直到队尾元素大于新增元素,再新增元素</li></ul><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span>[] maxSlidingWindow(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> k) {</span><br><span class="line"> <span class="keyword">if</span> (nums.length == <span class="number">0</span> || nums.length<k){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">0</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 单调递减队列,记录元素下标</span></span><br><span class="line"> Deque<Integer> deque = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> <span class="comment">// 记录每个窗口的最大值</span></span><br><span class="line"> List<Integer> res = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> <span class="comment">// 遍历所有元素</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < nums.length; i++) {</span><br><span class="line"> <span class="comment">// 单调队列不为空并且队首元素不在窗口范围内时就弹出队首元素</span></span><br><span class="line"> <span class="keyword">if</span> (!deque.isEmpty() && i-deque.peek()>=k){</span><br><span class="line"> deque.remove();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 单调队列不为空并且新增元素大于等于队尾元素就不断弹出队尾元素</span></span><br><span class="line"> <span class="keyword">while</span>(!deque.isEmpty() && nums[i]>=nums[deque.peekLast()]){</span><br><span class="line"> deque.removeLast();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 新增元素</span></span><br><span class="line"> deque.add(i);</span><br><span class="line"> <span class="comment">// 形成相应大小的滑动窗口范围时记录单调队列的队首元素</span></span><br><span class="line"> <span class="keyword">if</span> (i>=k-<span class="number">1</span>){</span><br><span class="line"> res.add(nums[deque.peek()]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 返回结果</span></span><br><span class="line"> <span class="keyword">return</span> res.stream().mapToInt(Integer::intValue).toArray();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="队列的最大值"><a href="#队列的最大值" class="headerlink" title="队列的最大值"></a>队列的最大值</h2><p><a href="https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof/">剑指 Offer 59 - II. 队列的最大值 - 力扣(LeetCode) (leetcode-cn.com)</a></p><h3 id="思路-1"><a href="#思路-1" class="headerlink" title="思路"></a>思路</h3><p>一个队列用于记录元素,一个单调队列用于记录队列中的最值</p><h3 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MaxQueue</span> </span>{</span><br><span class="line"> <span class="comment">// 队列</span></span><br><span class="line"> Queue<Integer> queue = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">// 单调递减队列</span></span><br><span class="line"> Deque<Integer> deque = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">// 构造函数</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MaxQueue</span><span class="params">()</span> </span>{</span><br><span class="line"> queue = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> deque = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取最大值</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">max_value</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 如果单调队列为空</span></span><br><span class="line"> <span class="keyword">if</span> (deque.isEmpty()){</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 返回单调队列队首元素</span></span><br><span class="line"> <span class="keyword">return</span> deque.peek();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 存放元素</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">push_back</span><span class="params">(<span class="keyword">int</span> value)</span> </span>{</span><br><span class="line"> <span class="comment">// 在队列中添加元素</span></span><br><span class="line"> queue.add(value);</span><br><span class="line"> <span class="comment">// 在单调递减队列中弹出小于等于新增元素的队尾元素</span></span><br><span class="line"> <span class="keyword">while</span> (!deque.isEmpty() && deque.peekLast()<=value){</span><br><span class="line"> deque.pollLast();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 在单调队列队尾添加元素</span></span><br><span class="line"> deque.addLast(value);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 弹出元素</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">pop_front</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 如果队列为空</span></span><br><span class="line"> <span class="keyword">if</span> (queue.isEmpty()){</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果队列队首元素等于单调队列队首元素</span></span><br><span class="line"> <span class="keyword">if</span> (queue.peek().equals(deque.peekFirst())){</span><br><span class="line"> <span class="comment">// 弹出单调队列队首元素</span></span><br><span class="line"> deque.pollFirst();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 返回队列队首元素</span></span><br><span class="line"> <span class="keyword">return</span> queue.poll();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
<tag> 队列 </tag>
</tags>
</entry>
<entry>
<title>给你一个树的后序遍历集,判断其是否为二叉搜索树</title>
<link href="/2022/01/28/%E7%BB%99%E4%BD%A0%E4%B8%80%E4%B8%AA%E6%A0%91%E7%9A%84%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86%E9%9B%86%EF%BC%8C%E5%88%A4%E6%96%AD%E5%85%B6%E6%98%AF%E5%90%A6%E4%B8%BA%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/"/>
<url>/2022/01/28/%E7%BB%99%E4%BD%A0%E4%B8%80%E4%B8%AA%E6%A0%91%E7%9A%84%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86%E9%9B%86%EF%BC%8C%E5%88%A4%E6%96%AD%E5%85%B6%E6%98%AF%E5%90%A6%E4%B8%BA%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</url>
<content type="html"><![CDATA[<h1 id="给你一个树的后序遍历集,判断其是否为二叉搜索树"><a href="#给你一个树的后序遍历集,判断其是否为二叉搜索树" class="headerlink" title="给你一个树的后序遍历集,判断其是否为二叉搜索树"></a>给你一个树的后序遍历集,判断其是否为二叉搜索树</h1><p>力扣链接:<a href="https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/">剑指 Offer 33. 二叉搜索树的后序遍历序列 - 力扣(LeetCode) (leetcode-cn.com)</a></p><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>已知后序遍历为:左子树->右子树->根结点,将其后序遍历结果数组反转后变为:根结点->右子树->左子树</p><p>如果其符合二叉搜索树的定义,那么它的右子树必然比根结点大,左子树必然比根结点小</p><p>我们使用一个单调栈(单调递增),对于根结点和右子树来说其满足单调栈的条件,对于左子树来说则不满足单调栈的条件,</p><p>当遇到数组元素小于单调栈顶部元素时,我们可知现在已经遍历到树的左子树部分,则弹出树的根结点和右子树,直到数组元素大于等于单调栈顶部元素</p><p><code>为什么要直到数组元素大于等于单调栈顶部元素,而不是直接清空单调栈,因为右子树的左子树也要比根结点大</code></p><p>对左子树进行同样的操作。</p><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">verifyPostorder</span><span class="params">(<span class="keyword">int</span>[] postorder)</span> </span>{</span><br><span class="line"> <span class="comment">// 记录前一个根结点的值</span></span><br><span class="line"> <span class="keyword">int</span> root = Integer.MAX_VALUE;</span><br><span class="line"> <span class="comment">// 单调栈</span></span><br><span class="line"> Stack<Integer> stack = <span class="keyword">new</span> Stack<>();</span><br><span class="line"> <span class="comment">// 从后往前遍历数组</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = postorder.length-<span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="comment">// 如果当前结点值大于前一个根结点的值,则不满足右子树的左子树也要比根结点大</span></span><br><span class="line"> <span class="comment">// 即不满足二叉搜索树的定义,返回false</span></span><br><span class="line"> <span class="keyword">if</span> (postorder[i]>root){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 此时已经遍历完根结点和右子树,遍历到左子树</span></span><br><span class="line"> <span class="keyword">while</span>(!stack.isEmpty() && stack.peek()>postorder[i]){</span><br><span class="line"> <span class="comment">// 弹出栈元素,并记录根结点的值</span></span><br><span class="line"> root = stack.pop();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 往单调栈中添加元素</span></span><br><span class="line"> stack.add(postorder[i]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>例图:<img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/20220128182832.png?x-oss-process=image/watermark,text_UGlnd2FudEFjYXQ=,type_ZHJvaWRzYW5zZmFsbGJhY2s,size_15,shadow_50,t_85,g_se,x_10,y_10,color_ffffff" alt="例图"></p>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
<tag> 树 </tag>
</tags>
</entry>
<entry>
<title>根据前序遍历和中序遍历结果重构二叉树</title>
<link href="/2022/01/27/%E6%A0%B9%E6%8D%AE%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E5%92%8C%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E7%BB%93%E6%9E%9C%E9%87%8D%E6%9E%84%E4%BA%8C%E5%8F%89%E6%A0%91/"/>
<url>/2022/01/27/%E6%A0%B9%E6%8D%AE%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E5%92%8C%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E7%BB%93%E6%9E%9C%E9%87%8D%E6%9E%84%E4%BA%8C%E5%8F%89%E6%A0%91/</url>
<content type="html"><![CDATA[<h1 id="根据前序遍历和中序遍历结果重构二叉树"><a href="#根据前序遍历和中序遍历结果重构二叉树" class="headerlink" title="根据前序遍历和中序遍历结果重构二叉树"></a>根据前序遍历和中序遍历结果重构二叉树</h1><p>力扣原题链接:<a href="https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/">剑指 Offer 07. 重建二叉树 - 力扣(LeetCode) (leetcode-cn.com)</a></p><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>中序遍历结果为左子树->根结点->右子树</p><p>由此我们可以确定树的范围,左子树范围为[0,根结点),根结点,(根结点,中序遍历结果数组长度-1]</p><p>再根据前序遍历结果确定根结点位置,前序遍历结果为根结点->左子树->右子树</p><p>然后递归就行了</p><p>递归出口为:当树范围的左边界大于右边界时,说明该结点为null结点</p><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * public class TreeNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * TreeNode left;</span></span><br><span class="line"><span class="comment"> * TreeNode right;</span></span><br><span class="line"><span class="comment"> * TreeNode(int x) { val = x; }</span></span><br><span class="line"><span class="comment"> * }</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">// 前序遍历结果</span></span><br><span class="line"> <span class="keyword">int</span>[] preorder;</span><br><span class="line"> <span class="comment">// 存放结点位置,用于确定子树边界</span></span><br><span class="line"> HashMap<Integer, Integer> map = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> <span class="function"><span class="keyword">public</span> TreeNode <span class="title">buildTree</span><span class="params">(<span class="keyword">int</span>[] preorder, <span class="keyword">int</span>[] inorder)</span> </span>{</span><br><span class="line"> <span class="comment">// 前序遍历结果</span></span><br><span class="line"> <span class="keyword">this</span>.preorder = preorder;</span><br><span class="line"> <span class="comment">// 存放结点位置</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < inorder.length; i++) {</span><br><span class="line"> <span class="comment">// Map<结点值,结点位置></span></span><br><span class="line"> map.put(inorder[i],i);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 根结点根据前序遍历结果确定,左右边界根据中序遍历结果确定</span></span><br><span class="line"> <span class="comment">// 根结点为数组第一个元素,左边界为0,右边界为数组长度-1</span></span><br><span class="line"> <span class="keyword">return</span> build(<span class="number">0</span>,<span class="number">0</span>,preorder.length-<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 重构二叉树</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> TreeNode <span class="title">build</span><span class="params">(<span class="keyword">int</span> root,<span class="keyword">int</span> left,<span class="keyword">int</span> right)</span></span>{</span><br><span class="line"> <span class="comment">// 递归出口,说明该结点为null结点</span></span><br><span class="line"> <span class="keyword">if</span> (left>right){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 新建根结点</span></span><br><span class="line"> TreeNode node = <span class="keyword">new</span> TreeNode(preorder[root]);</span><br><span class="line"> <span class="comment">// 根据根结点确定子树边界</span></span><br><span class="line"> <span class="keyword">int</span> i = map.get(preorder[root]);</span><br><span class="line"> <span class="comment">// 根结点根据前序遍历结果确定,左右边界根据中序遍历结果确定</span></span><br><span class="line"> <span class="comment">// 根结点的 左子树的根结点为 前序遍历结果中 根结点的下一个结点</span></span><br><span class="line"> <span class="comment">// 左子树的边界分别为 根结点的左边界 和 中序遍历结果中根结点的前一个结点</span></span><br><span class="line"> node.left = build(root+<span class="number">1</span>,left,i-<span class="number">1</span>);</span><br><span class="line"> <span class="comment">// 根结点的 右子树的根结点为 前序遍历结果中 (根结点+左子树)的下一个结点</span></span><br><span class="line"> <span class="comment">// 右子树的边界分别为 中序遍历结果中根结点的下一个结点 和 根结点的右边界</span></span><br><span class="line"> node.right = build(root+<span class="number">1</span>+i-left,i+<span class="number">1</span>,right);</span><br><span class="line"> <span class="comment">// 返回根结点</span></span><br><span class="line"> <span class="keyword">return</span> node;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
<tag> 树 </tag>
</tags>
</entry>
<entry>
<title>2021年全年理财小结</title>
<link href="/2022/01/12/2021%E5%B9%B4%E5%85%A8%E5%B9%B4%E7%90%86%E8%B4%A2%E5%B0%8F%E7%BB%93/"/>
<url>/2022/01/12/2021%E5%B9%B4%E5%85%A8%E5%B9%B4%E7%90%86%E8%B4%A2%E5%B0%8F%E7%BB%93/</url>
<content type="html"><![CDATA[<h1 id="2021年全年理财小结"><a href="#2021年全年理财小结" class="headerlink" title="2021年全年理财小结"></a>2021年全年理财小结</h1><h2 id="去年"><a href="#去年" class="headerlink" title="去年"></a>去年</h2><p>今年收益与创业板相当,下半年几乎全白玩</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/QQ%E5%9B%BE%E7%89%8720220112173432.png" style="zoom: 33%;" /><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2><p>将仓位逐渐过渡到中概互联、科创50和传媒</p>]]></content>
<categories>
<category> 理财 </category>
</categories>
<tags>
<tag> 基金 </tag>
</tags>
</entry>
<entry>
<title>节假日前闲钱购买国债逆回购</title>
<link href="/2021/12/31/%E8%8A%82%E5%81%87%E6%97%A5%E5%89%8D%E9%97%B2%E9%92%B1%E8%B4%AD%E4%B9%B0%E5%9B%BD%E5%80%BA%E9%80%86%E5%9B%9E%E8%B4%AD/"/>
<url>/2021/12/31/%E8%8A%82%E5%81%87%E6%97%A5%E5%89%8D%E9%97%B2%E9%92%B1%E8%B4%AD%E4%B9%B0%E5%9B%BD%E5%80%BA%E9%80%86%E5%9B%9E%E8%B4%AD/</url>
<content type="html"><![CDATA[<h1 id="节假日前闲钱购买国债逆回购"><a href="#节假日前闲钱购买国债逆回购" class="headerlink" title="节假日前闲钱购买国债逆回购"></a>节假日前闲钱购买国债逆回购</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p><a href="https://zhuanlan.zhihu.com/p/35244182">史上最全的国债逆回购讲解(附操作) - 知乎 (zhihu.com)</a></p><p><a href="https://www.zhihu.com/question/54385280">国债逆回购是什么,怎么操作,收益如何? - 知乎 (zhihu.com)</a></p><p>如果还不懂的请自行百度</p><h2 id="种类"><a href="#种类" class="headerlink" title="种类"></a>种类</h2><p>分为深市和沪市,沪市门槛较高,相对的利率也较高</p><p>深市:</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%287%29.png" alt="image-20211230113710056" style="zoom:50%;" /><p>沪市:</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%288%29.png" alt="image-20211230114203304" style="zoom:50%;" /><h2 id="预期收益"><a href="#预期收益" class="headerlink" title="预期收益"></a>预期收益</h2><p>其预期收益是动态变化的,例如:</p><p>在2021.12.30上午8点58</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%286%29.png" alt="image-20211230113605086" style="zoom:50%;" /><p>在2021.12.30上午10点40</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%287%29.png" alt="image-20211230113710056" style="zoom:50%;" /><p>当购买了具体某一期逆回购之后,其收益就可以确定了</p><h2 id="推荐购买时间"><a href="#推荐购买时间" class="headerlink" title="推荐购买时间"></a>推荐购买时间</h2><p>国债逆回购一般<code>在放假前两天</code>利率波动大,<code>在越重大的节假日前其波动越大</code></p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%281%29.png" alt="image-20211230114909015" style="zoom:50%;" /><h2 id="是否值得长期持有"><a href="#是否值得长期持有" class="headerlink" title="是否值得长期持有"></a>是否值得长期持有</h2><p><code>不是很推荐长期持有</code>。因为逆回购短期波动较大,长期波动较小。</p><ul><li>如果想要获得超额收益,不推荐长期持有;</li><li>如果首先考虑的是保障资金安全,那么推荐长期持有,因为逆回购的安全性很高</li></ul><h2 id="推荐购买金额"><a href="#推荐购买金额" class="headerlink" title="推荐购买金额"></a>推荐购买金额</h2><p>只推荐使用<code>券商账户中的闲钱</code>进行购买</p><ul><li><p>逆回购短期波动大,才会有相对较高的收益</p></li><li><p>其次根据你购买的品种不同,其可用和可取的时间也不一定相同</p><ul><li>例如我在2021.12.30购买了1天期的逆回购,我的可用时间是2021.12.31,但我的可取时间是2022.01.04</li><li><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%282%29.png" alt="image-20211230140459685" style="zoom:50%;" /></li></ul></li><li><p>为什么推荐使用券商账户中的闲钱购买</p><ul><li>还是根据上面的例子,在本交易日内我不准备进行其他交易,我购买了<code>1天期的逆回购</code>之后,其资金仍可以用于第二天的交易使用</li></ul></li><li><p>为什么要强调是券商账户了</p><ul><li>还是根据上面的例子,如果你不是券商账户中的闲钱,则需要等到节假日过后,即2022.01.04才能将这笔钱提现到银行卡中</li></ul></li></ul><h2 id="如何查看具体收益"><a href="#如何查看具体收益" class="headerlink" title="如何查看具体收益"></a>如何查看具体收益</h2><p>当你购买之后,你可以在国债逆回购中的<code>我的逆回购</code>查看</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%283%29.png" alt="image-20211230141513504" style="zoom:100%;" /><h2 id="如何购买"><a href="#如何购买" class="headerlink" title="如何购买"></a>如何购买</h2><p><code>各大券商app的国债逆回购板块</code>,请先自行注册券商账户</p><p>例如:东方财富手机端</p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/image-111%20%284%29.png" alt="image-20211230113157123" style="zoom:50%;" />]]></content>
<categories>
<category> 理财 </category>
</categories>
<tags>
<tag> 基金 </tag>
</tags>
</entry>
<entry>
<title>力扣LFU缓存题解</title>
<link href="/2021/12/21/%E5%8A%9B%E6%89%A3LFU%E7%BC%93%E5%AD%98%E9%A2%98%E8%A7%A3/"/>
<url>/2021/12/21/%E5%8A%9B%E6%89%A3LFU%E7%BC%93%E5%AD%98%E9%A2%98%E8%A7%A3/</url>
<content type="html"><![CDATA[<h1 id="力扣LFU缓存题解"><a href="#力扣LFU缓存题解" class="headerlink" title="力扣LFU缓存题解"></a>力扣LFU缓存题解</h1><p>题目链接:<a href="https://leetcode-cn.com/problems/lfu-cache/">460. LFU 缓存 - 力扣(LeetCode) (leetcode-cn.com)</a></p><p>参考:<a href="https://leetcode-cn.com/problems/lfu-cache/solution/java-13ms-shuang-100-shuang-xiang-lian-biao-duo-ji/">Java 13ms 双100% 双向链表 多解法超全😂 - LFU 缓存 - 力扣(LeetCode) (leetcode-cn.com)</a>,<a href="https://leetcode-cn.com/problems/lfu-cache/solution/chao-xiang-xi-tu-jie-dong-tu-yan-shi-460-lfuhuan-c/">超详细图解+动图演示 460. LFU缓存 - LFU 缓存 - 力扣(LeetCode) (leetcode-cn.com)</a></p><p>思路:与LRU思路类似,Node结点中增加一个属性freq用于表示该项的使用次数,双向链表与LRU中的一致,LRUCache是Map+双向链表,而LFUCache是Map+Map<freq,双向链表>,因为LRU只需找出最久未使用的Node,而LFU需要找出使用次数最少(如果有多个使用次数最少的,则找出其中最久未使用的)</p><h2 id="LRU对照学习"><a href="#LRU对照学习" class="headerlink" title="LRU对照学习"></a>LRU对照学习</h2><p>详情请参考另一篇<a href="https://www.zhbblog.top/2021/08/16/%E5%8A%9B%E6%89%A3LRU%E7%BC%93%E5%AD%98%E9%A2%98%E8%A7%A3/">力扣LRU缓存题解 </a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LRU</span> </span>{</span><br><span class="line"> DoubleLinkedList list;</span><br><span class="line"> HashMap<Integer, Node> map;</span><br><span class="line"> <span class="keyword">int</span> cap;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LRU</span><span class="params">(<span class="keyword">int</span> cap)</span> </span>{</span><br><span class="line"> list = <span class="keyword">new</span> DoubleLinkedList();</span><br><span class="line"> map = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> <span class="keyword">this</span>.cap = cap;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">get</span><span class="params">(<span class="keyword">int</span> key)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!map.containsKey(key)) {</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> value = map.get(key).value;</span><br><span class="line"> put(key, value);</span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(<span class="keyword">int</span> key, <span class="keyword">int</span> value)</span> </span>{</span><br><span class="line"> Node newNode = <span class="keyword">new</span> Node(key, value);</span><br><span class="line"> <span class="keyword">if</span> (map.containsKey(key)) {</span><br><span class="line"> list.delete(map.get(key));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (map.size() > cap) {</span><br><span class="line"> map.remove(list.delLast());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> list.addFirst(newNode);</span><br><span class="line"> map.put(key, newNode);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DoubleLinkedList</span> </span>{</span><br><span class="line"> Node head;</span><br><span class="line"> Node tail;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">DoubleLinkedList</span><span class="params">()</span> </span>{</span><br><span class="line"> head = <span class="keyword">new</span> Node(-<span class="number">1</span>, -<span class="number">1</span>);</span><br><span class="line"> tail = <span class="keyword">new</span> Node(-<span class="number">1</span>, -<span class="number">1</span>);</span><br><span class="line"> head.next = tail;</span><br><span class="line"> tail.prev = head;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addFirst</span><span class="params">(Node newNode)</span> </span>{</span><br><span class="line"> newNode.next = head.next;</span><br><span class="line"> newNode.prev = head;</span><br><span class="line"> head.next.prev = newNode;</span><br><span class="line"> head.next = newNode;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Node node)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> res = node.value;</span><br><span class="line"> node.prev.next = node.next;</span><br><span class="line"> node.next.prev = node.prev;</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delLast</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (head.next == tail) {</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> delete(tail.prev);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> key;</span><br><span class="line"> <span class="keyword">int</span> value;</span><br><span class="line"> Node prev;</span><br><span class="line"> Node next;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, <span class="keyword">int</span> value)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> <span class="keyword">this</span>.value = value;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="LFU"><a href="#LFU" class="headerlink" title="LFU"></a>LFU</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LFUCache</span> </span>{</span><br><span class="line"> <span class="comment">// key为项的使用次数,value为相同使用次数的Node结点组成的双向链表</span></span><br><span class="line"> HashMap<Integer, DoubleLinkedList> freqMap;</span><br><span class="line"> <span class="comment">// key为key,value为Node结点</span></span><br><span class="line"> HashMap<Integer, Node> map;</span><br><span class="line"> <span class="comment">// 容量</span></span><br><span class="line"> <span class="keyword">int</span> cap;</span><br><span class="line"> <span class="comment">// 当前元素个数</span></span><br><span class="line"> <span class="keyword">int</span> size;</span><br><span class="line"> <span class="comment">// 所有元素中的最少使用次数</span></span><br><span class="line"> <span class="keyword">int</span> min;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 初始化</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> capacity</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LFUCache</span><span class="params">(<span class="keyword">int</span> capacity)</span> </span>{</span><br><span class="line"> <span class="comment">// 新建map</span></span><br><span class="line"> map = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> <span class="comment">// 新建freqMap</span></span><br><span class="line"> freqMap = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> <span class="comment">// 容量</span></span><br><span class="line"> <span class="keyword">this</span>.cap = capacity;</span><br><span class="line"> <span class="comment">// 当前元素个数</span></span><br><span class="line"> size = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// 最少使用次数为0</span></span><br><span class="line"> min = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过key,获取相应的value的值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> key</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">get</span><span class="params">(<span class="keyword">int</span> key)</span> </span>{</span><br><span class="line"> <span class="comment">// 如果map中不存在,则在freqMap也不存在,直接返回-1</span></span><br><span class="line"> <span class="keyword">if</span> (!map.containsKey(key)){</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 获取结点</span></span><br><span class="line"> Node node = map.get(key);</span><br><span class="line"> <span class="comment">// 获取value</span></span><br><span class="line"> <span class="keyword">int</span> value = node.value;</span><br><span class="line"> <span class="comment">// 使该结点的使用次数增加1</span></span><br><span class="line"> freqInc(node);</span><br><span class="line"> <span class="comment">// 返回value</span></span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 放入对应key、value的结点</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> key</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(<span class="keyword">int</span> key, <span class="keyword">int</span> value)</span> </span>{</span><br><span class="line"> <span class="comment">// 如果LFU容量为0,直接返回</span></span><br><span class="line"> <span class="keyword">if</span> (cap==<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 从map中获取结点</span></span><br><span class="line"> Node node = map.get(key);</span><br><span class="line"> <span class="comment">// 如果存在该结点</span></span><br><span class="line"> <span class="keyword">if</span> (node!=<span class="keyword">null</span>){</span><br><span class="line"> <span class="comment">// 更改结点的value</span></span><br><span class="line"> node.value=value;</span><br><span class="line"> <span class="comment">// 并增加其结点使用次数</span></span><br><span class="line"> freqInc(node);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果不存在该结点</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 如果当前元素个数大于等于LFU容量</span></span><br><span class="line"> <span class="keyword">if</span> (size >= cap){</span><br><span class="line"> <span class="comment">// 获取使用次数最少的结点的双向链表</span></span><br><span class="line"> DoubleLinkedList minList = freqMap.get(min);</span><br><span class="line"> <span class="comment">// 从map中移除最少最久未使用的结点</span></span><br><span class="line"> map.remove(minList.tail.prev.key);</span><br><span class="line"> <span class="comment">// 从双向链表中删除该结点</span></span><br><span class="line"> minList.delLast();</span><br><span class="line"> <span class="comment">// 当前元素个数减1</span></span><br><span class="line"> size--;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 新建node结点</span></span><br><span class="line"> Node newNode = <span class="keyword">new</span> Node(key, value);</span><br><span class="line"> <span class="comment">// 在map中放入结点</span></span><br><span class="line"> map.put(key,newNode);</span><br><span class="line"> <span class="comment">// 获取使用次数为1的双向链表</span></span><br><span class="line"> DoubleLinkedList list = freqMap.get(<span class="number">1</span>);</span><br><span class="line"> <span class="comment">// 如果双向链表为空</span></span><br><span class="line"> <span class="keyword">if</span> (list==<span class="keyword">null</span>){</span><br><span class="line"> <span class="comment">// 创建双向链表</span></span><br><span class="line"> list = <span class="keyword">new</span> DoubleLinkedList();</span><br><span class="line"> <span class="comment">// 将双向链表放入freqMap中</span></span><br><span class="line"> freqMap.put(<span class="number">1</span>,list);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 将该结点加入到对应的双向链表中</span></span><br><span class="line"> list.addFirst(newNode);</span><br><span class="line"> <span class="comment">// 当前元素个数加1</span></span><br><span class="line"> size++;</span><br><span class="line"> <span class="comment">// 当前最少使用次数为1</span></span><br><span class="line"> min=<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将传入结点的使用次数自增1,并将其移动到相应使用次数的双向链表</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> node</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">freqInc</span><span class="params">(Node node)</span></span>{</span><br><span class="line"> <span class="comment">// 获取当前结点的使用次数</span></span><br><span class="line"> <span class="keyword">int</span> freq = node.freq;</span><br><span class="line"> <span class="comment">// 获取对应使用次数的双向链表</span></span><br><span class="line"> DoubleLinkedList list = freqMap.get(freq);</span><br><span class="line"> <span class="comment">// 在双向链表中删除该结点</span></span><br><span class="line"> list.delete(node);</span><br><span class="line"> <span class="comment">// 如果删除的是使用次数最少的双向链表的最后一个结点</span></span><br><span class="line"> <span class="keyword">if</span> (min==freq && list.head.next==list.tail){</span><br><span class="line"> min++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 结点使用次数自增1</span></span><br><span class="line"> freq++;</span><br><span class="line"> node.freq++;</span><br><span class="line"> <span class="comment">// 再次获取对应使用次数的双向链表</span></span><br><span class="line"> list = freqMap.get(freq);</span><br><span class="line"> <span class="comment">// 如果双向链表为空</span></span><br><span class="line"> <span class="keyword">if</span> (list==<span class="keyword">null</span>){</span><br><span class="line"> <span class="comment">// 创建双向链表</span></span><br><span class="line"> list = <span class="keyword">new</span> DoubleLinkedList();</span><br><span class="line"> <span class="comment">// 将双向链表放入freqMap中</span></span><br><span class="line"> freqMap.put(freq,list);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 将该结点加入到对应的双向链表中</span></span><br><span class="line"> list.addFirst(node);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义的双向链表</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DoubleLinkedList</span> </span>{</span><br><span class="line"> <span class="comment">// 双向链表结构</span></span><br><span class="line"> <span class="comment">// next指针:head -> 第一个结点 -> 第二个结点 -> ... -> tail</span></span><br><span class="line"> <span class="comment">// prev指针:head <- 第一个结点 <- 第二个结点 <- ... <- tail</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 双向链表的头结点</span></span><br><span class="line"> Node head;</span><br><span class="line"> <span class="comment">// 双向链表的尾结点</span></span><br><span class="line"> Node tail;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 新建双向链表</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">DoubleLinkedList</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 新建头结点</span></span><br><span class="line"> head = <span class="keyword">new</span> Node(-<span class="number">1</span>, -<span class="number">1</span>);</span><br><span class="line"> <span class="comment">// 新建尾结点</span></span><br><span class="line"> tail = <span class="keyword">new</span> Node(-<span class="number">1</span>, -<span class="number">1</span>);</span><br><span class="line"> <span class="comment">// 头结点的next指针指向尾结点</span></span><br><span class="line"> head.next = tail;</span><br><span class="line"> <span class="comment">// 尾结点的prev指针指向头结点</span></span><br><span class="line"> tail.prev = head;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将结点加入双向链表的头部</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addFirst</span><span class="params">(Node newNode)</span> </span>{</span><br><span class="line"> <span class="comment">// 新结点的prev指针指向头结点</span></span><br><span class="line"> newNode.prev = head;</span><br><span class="line"> <span class="comment">// 新结点的next指针指向头结点的下一个结点</span></span><br><span class="line"> newNode.next = head.next;</span><br><span class="line"> <span class="comment">// 头结点的下一个结点的prev指针指向新结点</span></span><br><span class="line"> head.next.prev = newNode;</span><br><span class="line"> <span class="comment">// 头结点的next指针指向新结点</span></span><br><span class="line"> head.next = newNode;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在双向链表中删除对应的结点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Node node)</span> </span>{</span><br><span class="line"> <span class="comment">// 拿到当前的结点的value</span></span><br><span class="line"> <span class="keyword">int</span> value = node.value;</span><br><span class="line"> <span class="comment">// 当前结点的下一个结点的prev指针指向当前结点的前一个结点</span></span><br><span class="line"> node.next.prev = node.prev;</span><br><span class="line"> <span class="comment">// 当前结点的前一个结点的next指针指向当前结点的下一个结点</span></span><br><span class="line"> node.prev.next = node.next;</span><br><span class="line"> <span class="comment">// 返回value</span></span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 删除尾结点的前一个结点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delLast</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 如果双向链表为空,直接返回-1</span></span><br><span class="line"> <span class="keyword">if</span> (head.next == tail) {</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 删除最后一个结点,即尾结点的前一个结点</span></span><br><span class="line"> <span class="keyword">return</span> delete(tail.prev);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义的node结点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>{</span><br><span class="line"> <span class="comment">// key</span></span><br><span class="line"> <span class="keyword">int</span> key;</span><br><span class="line"> <span class="comment">// value</span></span><br><span class="line"> <span class="keyword">int</span> value;</span><br><span class="line"> <span class="comment">// prev指针</span></span><br><span class="line"> Node prev;</span><br><span class="line"> <span class="comment">// next指针 </span></span><br><span class="line"> Node next;</span><br><span class="line"> <span class="comment">// 结点使用次数</span></span><br><span class="line"> <span class="keyword">int</span> freq;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建node结点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, <span class="keyword">int</span> value)</span> </span>{</span><br><span class="line"> <span class="comment">// key</span></span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> <span class="comment">// value</span></span><br><span class="line"> <span class="keyword">this</span>.value = value;</span><br><span class="line"> <span class="comment">// 结点使用次数为1</span></span><br><span class="line"> freq = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
</tags>
</entry>
<entry>
<title>docker常用命令</title>
<link href="/2021/11/28/docker%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/"/>
<url>/2021/11/28/docker%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</url>
<content type="html"><![CDATA[<h1 id="docker常用命令"><a href="#docker常用命令" class="headerlink" title="docker常用命令"></a>docker常用命令</h1><p>参考于:<a href="https://www.kuangstudy.com/">首页-KuangStudy</a></p><h2 id="帮助命令"><a href="#帮助命令" class="headerlink" title="帮助命令"></a>帮助命令</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">查看docker版本</span></span><br><span class="line">docker version</span><br><span class="line">docker -v</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">查看docker系统信息</span></span><br><span class="line">docker info</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">帮助</span></span><br><span class="line">docker --help</span><br></pre></td></tr></table></figure><h2 id="镜像命令"><a href="#镜像命令" class="headerlink" title="镜像命令"></a>镜像命令</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">查看镜像列表</span></span><br><span class="line">docker images </span><br><span class="line">等价于 docker image ls</span><br><span class="line"><span class="meta">#</span><span class="bash">可选参数</span></span><br><span class="line">-a #列出本地所有镜像 </span><br><span class="line">-q #只显示镜像id </span><br><span class="line">--digests #显示镜像的摘要信息</span><br><span class="line"><span class="meta">#</span><span class="bash">示例</span></span><br><span class="line">docker images -aq</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">搜索镜像</span></span><br><span class="line">docker search IMAGE_NAME:TAG</span><br><span class="line"><span class="meta">#</span><span class="bash">可选参数</span></span><br><span class="line">--filter=stars=50 : 列出收藏数不小于指定值的镜像。</span><br><span class="line"><span class="meta">#</span><span class="bash">示例</span></span><br><span class="line">docker search mysql --filter=stars=5000</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">拉取镜像</span></span><br><span class="line">docker pull IMAGE_NAME:TAG</span><br><span class="line"><span class="meta">#</span><span class="bash">示例</span></span><br><span class="line">docker pull mysql:8.0 #如不指定版本,默认指定latest</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">删除镜像</span></span><br><span class="line">docker rmi IMAGE_NAME:TAG</span><br><span class="line"><span class="meta">#</span><span class="bash">可选参数</span></span><br><span class="line">-f: 强制删除</span><br><span class="line"><span class="meta">#</span><span class="bash">示例</span></span><br><span class="line">docker rmi -f mysql:8.0</span><br><span class="line">docker rmi -f $(docker images -aq) #删除全部</span><br></pre></td></tr></table></figure><h2 id="容器命令"><a href="#容器命令" class="headerlink" title="容器命令"></a>容器命令</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">新建并启动容器</span></span><br><span class="line">docker run IMAGE_NAME</span><br><span class="line"><span class="meta">#</span><span class="bash">可用参数</span></span><br><span class="line">--name="Name" #给容器指定名字</span><br><span class="line">-d #以后台方式运行</span><br><span class="line">-i #以交互模式运行</span><br><span class="line">-t #新开一个端口</span><br><span class="line">-p #(小写)指定端口</span><br><span class="line">-P #(大写)随机端口</span><br><span class="line"><span class="meta">#</span><span class="bash">示例</span></span><br><span class="line">docker run --name=mycentos -it centos /bin/bash</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">查看所有容器</span></span><br><span class="line">docker ps</span><br><span class="line">等价于docker container ls</span><br><span class="line"><span class="meta">#</span><span class="bash">可选参数</span></span><br><span class="line">-a #所有容器</span><br><span class="line">-l #最近创建的容器</span><br><span class="line">-n=? #最近创建的n个容器</span><br><span class="line">-q #只显示容器编号</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">退出容器</span></span><br><span class="line">exit #退出并停止容器</span><br><span class="line">ctrl+P+Q #退出不停止容器</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">启动容器</span></span><br><span class="line">docker start CONTAINER_ID or CONTAINER_NAME </span><br><span class="line"><span class="meta">#</span><span class="bash">重启容器</span></span><br><span class="line">docker restart CONTAINER_ID or CONTAINER_NAME</span><br><span class="line"><span class="meta">#</span><span class="bash">停止容器</span></span><br><span class="line">docker stop CONTAINER_ID or CONTAINER_NAME</span><br><span class="line"><span class="meta">#</span><span class="bash">强行停止容器</span></span><br><span class="line">docker kill CONTAINER_ID or CONTAINER_NAME</span><br><span class="line"><span class="meta">#</span><span class="bash">删除容器</span></span><br><span class="line">docker rm CONTAINER_ID or CONTAINER_NAME</span><br><span class="line">docker rm -f $(docker ps -aq)#删除所有容器</span><br></pre></td></tr></table></figure><h2 id="其他常用命令"><a href="#其他常用命令" class="headerlink" title="其他常用命令"></a>其他常用命令</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">查看日志</span></span><br><span class="line">docker logs CONTAINER_ID</span><br><span class="line"><span class="meta">#</span><span class="bash">可选参数</span></span><br><span class="line">-t #显示时间戳</span><br><span class="line">-f #打印最新日志</span><br><span class="line">--tail #显示数量</span><br><span class="line"><span class="meta">#</span><span class="bash">示例</span></span><br><span class="line">docker logs -tf --tail 10 c8530dbbe3b4</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">查看容器中运行的进程信息</span></span><br><span class="line">docker top CONTAINER_ID</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">查看元数据</span></span><br><span class="line">docker inspect CONTAINER_ID/IMAGE_ID</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">进入正在运行的容器</span></span><br><span class="line">exec #在容器中打开新的终端,并且可以启动新的进程</span><br><span class="line">attach #直接进入容器启动命令的终端,不会启动新的进程</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash">从容器中拷贝文件到主机</span></span><br><span class="line">docker cp 容器id:容器内路径 目的主机路径</span><br></pre></td></tr></table></figure><h2 id="常用命令图"><a href="#常用命令图" class="headerlink" title="常用命令图"></a>常用命令图</h2><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/QQ%E5%9B%BE%E7%89%8720211128205309.png" alt="img"></p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/QQ%E6%88%AA%E5%9B%BE20211128214723.png" alt="img"></p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> docker </tag>
</tags>
</entry>
<entry>
<title>求1+1/2+1/3+2/3+1/4+2/+3/4+.....求和公式</title>
<link href="/2021/11/27/%E6%B1%821-1-2-1-3-2-3-1-4-2-3-4-%E6%B1%82%E5%92%8C%E5%85%AC%E5%BC%8F/"/>
<url>/2021/11/27/%E6%B1%821-1-2-1-3-2-3-1-4-2-3-4-%E6%B1%82%E5%92%8C%E5%85%AC%E5%BC%8F/</url>
<content type="html"><![CDATA[<h1 id="求1-1-2-1-3-2-3-1-4-2-3-4-…-求和公式"><a href="#求1-1-2-1-3-2-3-1-4-2-3-4-…-求和公式" class="headerlink" title="求1+1/2+1/3+2/3+1/4+2/+3/4+…..求和公式"></a>求1+1/2+1/3+2/3+1/4+2/+3/4+…..求和公式</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>1+(1/2)+(1/3+2/3)+(1/4+2/4+3/4)+(1/5+2/5+3/5+4/5)+(1/6+2/6+3/6+4/6+5/6)……+(1/n+2/n+……+(n-2/n)+(n-1/n))</p><h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202021-11-26%20163639.png" alt="img"></p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202021-11-26%20164523.png" alt="img"></p><h2 id="等差数列求和公式"><a href="#等差数列求和公式" class="headerlink" title="等差数列求和公式"></a>等差数列求和公式</h2><p>等差数列求和公式为:</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/7af40ad162d9f2d3dad39893b9ec8a136227cc88.png" alt="img"></p><h2 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h2><p>由题可知:a1=0,d=1</p><p>代入可得</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202021-11-26%20164925.png" alt="img"></p>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
</tags>
</entry>
<entry>
<title>两个人玩抛硬币的游戏,谁先抛到正面就获胜。那么先抛的人获胜概率为?</title>
<link href="/2021/11/25/%E4%B8%A4%E4%B8%AA%E4%BA%BA%E7%8E%A9%E6%8A%9B%E7%A1%AC%E5%B8%81%E7%9A%84%E6%B8%B8%E6%88%8F%EF%BC%8C%E8%B0%81%E5%85%88%E6%8A%9B%E5%88%B0%E6%AD%A3%E9%9D%A2%E5%B0%B1%E8%8E%B7%E8%83%9C%E3%80%82%E9%82%A3%E4%B9%88%E5%85%88%E6%8A%9B%E7%9A%84%E4%BA%BA%E8%8E%B7%E8%83%9C%E6%A6%82%E7%8E%87%E4%B8%BA%EF%BC%9F/"/>
<url>/2021/11/25/%E4%B8%A4%E4%B8%AA%E4%BA%BA%E7%8E%A9%E6%8A%9B%E7%A1%AC%E5%B8%81%E7%9A%84%E6%B8%B8%E6%88%8F%EF%BC%8C%E8%B0%81%E5%85%88%E6%8A%9B%E5%88%B0%E6%AD%A3%E9%9D%A2%E5%B0%B1%E8%8E%B7%E8%83%9C%E3%80%82%E9%82%A3%E4%B9%88%E5%85%88%E6%8A%9B%E7%9A%84%E4%BA%BA%E8%8E%B7%E8%83%9C%E6%A6%82%E7%8E%87%E4%B8%BA%EF%BC%9F/</url>
<content type="html"><![CDATA[<h1 id="两个人玩抛硬币的游戏,谁先抛到正面就获胜。那么先抛的人获胜概率为?"><a href="#两个人玩抛硬币的游戏,谁先抛到正面就获胜。那么先抛的人获胜概率为?" class="headerlink" title="两个人玩抛硬币的游戏,谁先抛到正面就获胜。那么先抛的人获胜概率为?"></a>两个人玩抛硬币的游戏,谁先抛到正面就获胜。那么先抛的人获胜概率为?</h1><h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>先抛获胜情况:</p><ul><li>正</li><li>反反正</li><li>反反反反正</li><li>……</li><li>反反……正</li></ul><p>p=1/2 + 1/2^3 + 1/2^5 + …….</p><h2 id="等比数列求和公式"><a href="#等比数列求和公式" class="headerlink" title="等比数列求和公式"></a>等比数列求和公式</h2><p> 等比求和:</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/fce39d3a972946267dbfac0b3fe68d00.svg" alt="img"></p><p>①当q≠1时,</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/de92cc5e4793ae038e4297e83971154a.svg" alt="img"></p><p> 或</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/842fdf9264ec2466cebdc60ba3daf499.svg" alt="img"></p><p>②当q=1时,</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/32cbe4d2be1e2d1105cc59bd00b148fc.svg" alt="img"></p><h2 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h2><p>由题可知a1 = 1/2, q=1/4</p><p>代入<img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/de92cc5e4793ae038e4297e83971154a.svg" alt="img">可得</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/QQ%E6%88%AA%E5%9B%BE20211125214300.png" alt="img"><br>由于1/4^n当n趋近于无穷大时其约等于0,所以1-1/4^n约等于1,所以等式为</p><p><img src="https://zhb-blog-imgs.oss-cn-beijing.aliyuncs.com/QQ%E6%88%AA%E5%9B%BE20211125214313.png" alt="img"></p>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
</tags>
</entry>
<entry>
<title>分片断点上传及下载</title>
<link href="/2021/10/07/%E5%88%86%E7%89%87%E6%96%AD%E7%82%B9%E4%B8%8A%E4%BC%A0%E5%8F%8A%E4%B8%8B%E8%BD%BD/"/>
<url>/2021/10/07/%E5%88%86%E7%89%87%E6%96%AD%E7%82%B9%E4%B8%8A%E4%BC%A0%E5%8F%8A%E4%B8%8B%E8%BD%BD/</url>
<content type="html"><![CDATA[<h1 id="分片断点上传及下载"><a href="#分片断点上传及下载" class="headerlink" title="分片断点上传及下载"></a>分片断点上传及下载</h1><h2 id="分片上传"><a href="#分片上传" class="headerlink" title="分片上传"></a>分片上传</h2><h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#application.yml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"> <span class="attr">servlet:</span></span><br><span class="line"> <span class="attr">multipart:</span></span><br><span class="line"> <span class="attr">enabled:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!--pom.xml--></span></span><br><span class="line"><span class="tag"><<span class="name">dependencies</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-web<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-autoconfigure<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.5.5<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>commons-fileupload<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>commons-fileupload<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.4<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>commons-io<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>commons-io<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.5<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.httpcomponents<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>httpcore<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>4.4.14<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.httpcomponents<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>httpclient<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>4.5.13<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.projectlombok<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>lombok<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-test<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependencies</span>></span></span><br></pre></td></tr></table></figure><h3 id="前端文件"><a href="#前端文件" class="headerlink" title="前端文件"></a>前端文件</h3><p><img src="https://i.loli.net/2021/10/07/UsMnl6IS2fx97rV.png" alt="image-20211007101843728"></p><h3 id="前端代码"><a href="#前端代码" class="headerlink" title="前端代码"></a>前端代码</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>webuploader<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="comment"><!--引入CSS--></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">type</span>=<span class="string">"text/css"</span> <span class="attr">href</span>=<span class="string">"webuploader.css"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"jquery-1.11.1.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"webuploader.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">style</span>></span><span class="css"></span></span><br><span class="line"><span class="css"> <span class="selector-id">#upload-container</span>, <span class="selector-id">#upload-list</span>{<span class="attribute">width</span>: <span class="number">500px</span>; <span class="attribute">margin</span>: <span class="number">0</span> auto; }</span></span><br><span class="line"><span class="css"> <span class="selector-id">#upload-container</span>{<span class="attribute">cursor</span>: pointer; <span class="attribute">border-radius</span>: <span class="number">15px</span>; <span class="attribute">background</span>: <span class="number">#EEEFFF</span>; <span class="attribute">height</span>: <span class="number">200px</span>;}</span></span><br><span class="line"><span class="css"> <span class="selector-id">#upload-list</span>{<span class="attribute">height</span>: <span class="number">800px</span>; <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#EEE</span>; <span class="attribute">border-radius</span>: <span class="number">5px</span>; <span class="attribute">margin-top</span>: <span class="number">10px</span>; <span class="attribute">padding</span>: <span class="number">10px</span> <span class="number">20px</span>;}</span></span><br><span class="line"><span class="css"> <span class="selector-id">#upload-container</span>><span class="selector-tag">span</span>{<span class="attribute">widows</span>: <span class="number">100%</span>; <span class="attribute">text-align</span>: center; <span class="attribute">color</span>: gray; <span class="attribute">display</span>: block; <span class="attribute">padding-top</span>: <span class="number">15%</span>;}</span></span><br><span class="line"><span class="css"> <span class="selector-class">.upload-item</span>{<span class="attribute">margin-top</span>: <span class="number">5px</span>; <span class="attribute">padding-bottom</span>: <span class="number">5px</span>; <span class="attribute">border-bottom</span>: <span class="number">1px</span> dashed gray;}</span></span><br><span class="line"><span class="css"> <span class="selector-class">.percentage</span>{<span class="attribute">height</span>: <span class="number">5px</span>; <span class="attribute">background</span>: green;}</span></span><br><span class="line"><span class="css"> <span class="selector-class">.btn-delete</span>, <span class="selector-class">.btn-retry</span>{<span class="attribute">cursor</span>: pointer; <span class="attribute">color</span>: gray;}</span></span><br><span class="line"><span class="css"> <span class="selector-class">.btn-delete</span><span class="selector-pseudo">:hover</span>{<span class="attribute">color</span>: orange;}</span></span><br><span class="line"><span class="css"> <span class="selector-class">.btn-retry</span><span class="selector-pseudo">:hover</span>{<span class="attribute">color</span>: green;}</span></span><br><span class="line"><span class="css"></span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="comment"><!--引入JS--></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"upload-container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span>></span>点击或将文件拖拽至此上传<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"upload-list"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"picker"</span> <span class="attr">style</span>=<span class="string">"display: none;"</span>></span>点击上传文件<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></span><br><span class="line"><span class="javascript"> $(<span class="string">'#upload-container'</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="javascript"> $(<span class="string">"#picker"</span>).find(<span class="string">'input'</span>).click();</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> uploader = WebUploader.create({</span></span><br><span class="line"><span class="javascript"> <span class="attr">auto</span>: <span class="literal">true</span>,<span class="comment">// 选完文件后,是否自动上传。</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">swf</span>: <span class="string">'Uploader.swf'</span>,<span class="comment">// swf文件路径</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">server</span>: <span class="string">'http://localhost:8080/upload'</span>,<span class="comment">// 文件接收服务端。</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">dnd</span>: <span class="string">'#upload-container'</span>,</span></span><br><span class="line"><span class="javascript"> <span class="attr">pick</span>: <span class="string">'#picker'</span>,<span class="comment">// 内部根据当前运行是创建,可能是input元素,也可能是flash. 这里是div的id</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">multiple</span>: <span class="literal">true</span>, <span class="comment">// 选择多个</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">chunked</span>: <span class="literal">true</span>,<span class="comment">// 开启分片上传。</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">threads</span>: <span class="number">20</span>, <span class="comment">// 上传并发数。允许同时最大上传进程数。</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">method</span>: <span class="string">'POST'</span>, <span class="comment">// 文件上传方式,POST或者GET。</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">fileSizeLimit</span>: <span class="number">1024</span>*<span class="number">1024</span>*<span class="number">1024</span>*<span class="number">10</span>, <span class="comment">//验证文件总大小是否超出限制, 超出则不允许加入队列。10g</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">fileSingleSizeLimit</span>: <span class="number">1024</span>*<span class="number">1024</span>*<span class="number">1024</span>*<span class="number">5</span>, <span class="comment">//验证单个文件大小是否超出限制, 超出则不允许加入队列。5g</span></span></span><br><span class="line"><span class="javascript"> <span class="attr">fileVal</span>:<span class="string">'upload'</span> <span class="comment">// [默认值:'file'] 设置文件上传域的name。</span></span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> uploader.on(<span class="string">"beforeFileQueued"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">file</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file); <span class="comment">// 获取文件的后缀</span></span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> uploader.on(<span class="string">'fileQueued'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">file</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 选中文件时要做的事情,比如在页面中显示选中的文件并添加到文件列表,获取文件的大小,文件类型等</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file.ext); <span class="comment">// 获取文件的后缀</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file.size);<span class="comment">// 获取文件的大小</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file.name);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> html = <span class="string">'<div class="upload-item"><span>文件名:'</span>+file.name+<span class="string">'</span><span data-file_id="'</span>+file.id+<span class="string">'" class="btn-delete">删除</span><span data-file_id="'</span>+file.id+<span class="string">'" class="btn-retry">重试</span><div class="percentage '</span>+file.id+<span class="string">'" style="width: 0%;"></div></div>'</span>;</span></span><br><span class="line"><span class="javascript"> $(<span class="string">'#upload-list'</span>).append(html);</span></span><br><span class="line"><span class="javascript"> uploader.md5File( file )<span class="comment">//大文件秒传</span></span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 及时显示进度</span></span></span><br><span class="line"><span class="javascript"> .progress(<span class="function"><span class="keyword">function</span>(<span class="params">percentage</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'Percentage:'</span>, percentage);</span></span><br><span class="line"><span class="javascript"> })</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 完成</span></span></span><br><span class="line"><span class="javascript"> .then(<span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'md5 result:'</span>, val);</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> uploader.on(<span class="string">'uploadProgress'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">file, percentage</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(percentage * <span class="number">100</span> + <span class="string">'%'</span>);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> width = $(<span class="string">'.upload-item'</span>).width();</span></span><br><span class="line"><span class="javascript"> $(<span class="string">'.'</span>+file.id).width(width*percentage);</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> uploader.on(<span class="string">'uploadSuccess'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">file, response</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file.id+<span class="string">"传输成功"</span>);</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> uploader.on(<span class="string">'uploadError'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">file</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file);</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(file.id+<span class="string">'upload error'</span>)</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> $(<span class="string">'#upload-list'</span>).on(<span class="string">'click'</span>, <span class="string">'.upload-item .btn-delete'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 从文件队列中删除某个文件id</span></span></span><br><span class="line"><span class="javascript"> file_id = $(<span class="built_in">this</span>).data(<span class="string">'file_id'</span>);</span></span><br><span class="line"><span class="javascript"> <span class="comment">// uploader.removeFile(file_id); // 标记文件状态为已取消</span></span></span><br><span class="line"><span class="javascript"> uploader.removeFile(file_id, <span class="literal">true</span>); <span class="comment">// 从queue中删除</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(uploader.getFiles());</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> $(<span class="string">'#upload-list'</span>).on(<span class="string">'click'</span>, <span class="string">'.btn-retry'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> uploader.retry($(<span class="built_in">this</span>).data(<span class="string">'file_id'</span>));</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span></span><br><span class="line"><span class="javascript"> uploader.on(<span class="string">'uploadComplete'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">file</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(uploader.getFiles());</span></span><br><span class="line"><span class="javascript"> });</span></span><br><span class="line"><span class="javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><h3 id="后端代码"><a href="#后端代码" class="headerlink" title="后端代码"></a>后端代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分片上传下载</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: zh'b</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span>: 2021/10/6 15:03</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UploadAndDownloadController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 编码格式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String UTF8 =<span class="string">"utf-8"</span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 文件保存下载路径</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String UPLOAD_DOWNLOAD_PATH = <span class="string">"D:\\upload_test"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 服务器端实现分片上传</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@ResponseBody</span></span><br><span class="line"> <span class="meta">@RequestMapping("/upload")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">upload</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">//设置编码格式</span></span><br><span class="line"> response.setCharacterEncoding(UTF8);</span><br><span class="line"> <span class="comment">//当前分片</span></span><br><span class="line"> Integer schunk = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//总分片数</span></span><br><span class="line"> Integer schunks = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//名称</span></span><br><span class="line"> String name = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//保存路径</span></span><br><span class="line"> BufferedOutputStream os = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> DiskFileItemFactory factory = <span class="keyword">new</span> DiskFileItemFactory();</span><br><span class="line"> <span class="comment">//文件超出该大小写入磁盘</span></span><br><span class="line"> factory.setSizeThreshold(<span class="number">1024</span>);</span><br><span class="line"> <span class="comment">//设置保存路径</span></span><br><span class="line"> factory.setRepository(<span class="keyword">new</span> File(UPLOAD_DOWNLOAD_PATH));</span><br><span class="line"></span><br><span class="line"> <span class="comment">//解析request</span></span><br><span class="line"> ServletFileUpload uploaded = <span class="keyword">new</span> ServletFileUpload(factory);</span><br><span class="line"> <span class="comment">//单个文件不超过5g</span></span><br><span class="line"> uploaded.setFileSizeMax(<span class="number">5L</span> * <span class="number">1024L</span> * <span class="number">1024L</span> * <span class="number">1024L</span>);</span><br><span class="line"> <span class="comment">//总文件不超过10g</span></span><br><span class="line"> uploaded.setSizeMax(<span class="number">10L</span> * <span class="number">1024L</span> * <span class="number">1024L</span> * <span class="number">1024L</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//解析</span></span><br><span class="line"> List<FileItem> items = uploaded.parseRequest(request);</span><br><span class="line"> <span class="comment">//遍历所有表单属性</span></span><br><span class="line"> <span class="keyword">for</span> (FileItem item : items) {</span><br><span class="line"> <span class="comment">//判断是不是文件,true:表单属性,false:上传的文件</span></span><br><span class="line"> <span class="keyword">if</span>(item.isFormField()){</span><br><span class="line"> <span class="comment">//当前分片</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="string">"chunk"</span>.equals(item.getFieldName())){</span><br><span class="line"> schunk = Integer.parseInt(item.getString(UTF8));</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//总分片</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="string">"chunks"</span>.equals(item.getFieldName())){</span><br><span class="line"> schunks = Integer.parseInt(item.getString(UTF8));</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//名称</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="string">"name"</span>.equals(item.getFieldName())){</span><br><span class="line"> name = item.getString(UTF8);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//遍历所有上传文件</span></span><br><span class="line"> <span class="keyword">for</span>(FileItem item : items){</span><br><span class="line"> <span class="comment">//判断是不是文件,true:表单属性,false:上传的文件</span></span><br><span class="line"> <span class="keyword">if</span>(!item.isFormField()){</span><br><span class="line"> <span class="comment">//临时文件名</span></span><br><span class="line"> String temFileName = name;</span><br><span class="line"> <span class="comment">//名字不能为空</span></span><br><span class="line"> <span class="keyword">if</span>(name != <span class="keyword">null</span>){</span><br><span class="line"> <span class="comment">//当前分片为空,表示上传的是一个完整的文件</span></span><br><span class="line"> <span class="keyword">if</span>(schunk != <span class="keyword">null</span>){</span><br><span class="line"> <span class="comment">//修改临时文件名,因为当前分片文件只是完整文件的一个分片</span></span><br><span class="line"> temFileName = schunk +<span class="string">"_"</span>+name;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//查询磁盘中是否有当前分片文件</span></span><br><span class="line"> File temFile = <span class="keyword">new</span> File(UPLOAD_DOWNLOAD_PATH,temFileName);</span><br><span class="line"> <span class="comment">//断点续传,当前分片文件不在磁盘中</span></span><br><span class="line"> <span class="keyword">if</span>(!temFile.exists()){</span><br><span class="line"> <span class="comment">//写入当前分片文件到磁盘中</span></span><br><span class="line"> item.write(temFile);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//文件合并,合并所有分片文件</span></span><br><span class="line"> <span class="comment">//判断是否有分片、判断是否为最后一个分片</span></span><br><span class="line"> <span class="keyword">if</span>(schunk != <span class="keyword">null</span> && schunk.intValue() == schunks.intValue()-<span class="number">1</span>){</span><br><span class="line"> File tempFile = <span class="keyword">new</span> File(UPLOAD_DOWNLOAD_PATH,name);</span><br><span class="line"> os = <span class="keyword">new</span> BufferedOutputStream(<span class="keyword">new</span> FileOutputStream(tempFile));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span> ;i<schunks;i++){</span><br><span class="line"> <span class="comment">//获取分片</span></span><br><span class="line"> File file = <span class="keyword">new</span> File(UPLOAD_DOWNLOAD_PATH,i+<span class="string">"_"</span>+name);</span><br><span class="line"> <span class="comment">//判断当前分片是否已经就位</span></span><br><span class="line"> <span class="keyword">while</span>(!file.exists()){</span><br><span class="line"> Thread.sleep(<span class="number">100</span>);</span><br><span class="line"> file = <span class="keyword">new</span> File(UPLOAD_DOWNLOAD_PATH,i+<span class="string">"_"</span>+name);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//获取当前分片数据</span></span><br><span class="line"> <span class="keyword">byte</span>[] bytes = FileUtils.readFileToByteArray(file);</span><br><span class="line"> <span class="comment">//写入数据</span></span><br><span class="line"> os.write(bytes);</span><br><span class="line"> <span class="comment">//清空缓冲流中的数据</span></span><br><span class="line"> os.flush();</span><br><span class="line"> <span class="comment">//删除当前分片</span></span><br><span class="line"> file.delete();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//清空缓冲流中的数据</span></span><br><span class="line"> os.flush();</span><br><span class="line"> }</span><br><span class="line"> response.getWriter().write(<span class="string">"上传成功"</span>+name);</span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span> (os!=<span class="keyword">null</span>){</span><br><span class="line"> os.close();</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><p><img src="https://i.loli.net/2021/10/07/ms3dFSjvi7IrVEb.png" alt="image-20211007102110148"></p><p><img src="https://i.loli.net/2021/10/07/cibpy2V9YTqzCxt.png" alt="image-20211007102137588"></p><p><img src="https://i.loli.net/2021/10/07/Ir7a4SyFq6Cz1xj.png" alt="image-20211007102306094"></p><h2 id="分片下载"><a href="#分片下载" class="headerlink" title="分片下载"></a>分片下载</h2><h3 id="服务器端代码"><a href="#服务器端代码" class="headerlink" title="服务器端代码"></a>服务器端代码</h3><p><code>如果只实现了服务器代码,未实现客户端代码 则与 普通下载无任何差异</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分片上传下载</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: zh'b</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span>: 2021/10/6 15:03</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UploadAndDownloadController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 编码格式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String UTF8 =<span class="string">"utf-8"</span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 文件保存下载路径</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String UPLOAD_DOWNLOAD_PATH = <span class="string">"D:\\upload_test"</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 实现服务器端支持分片下载</span></span><br><span class="line"><span class="comment"> * 下载示例:http://localhost:8080/download?downloadFileName=JWT视频.mp4</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> downloadFileName</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@RequestMapping("/download")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">downLoadFile</span><span class="params">(HttpServletRequest request, HttpServletResponse response,String downloadFileName)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">//参数为空</span></span><br><span class="line"> <span class="keyword">if</span> (downloadFileName==<span class="keyword">null</span>|| <span class="string">""</span>.equals(downloadFileName)) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//读取文件</span></span><br><span class="line"> File file = <span class="keyword">new</span> File(UPLOAD_DOWNLOAD_PATH+<span class="string">"\\"</span>+downloadFileName);</span><br><span class="line"> <span class="comment">//设置响应编码格式</span></span><br><span class="line"> response.setCharacterEncoding(UTF8);</span><br><span class="line"> <span class="comment">//输入流</span></span><br><span class="line"> InputStream is = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//输出流</span></span><br><span class="line"> OutputStream os = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//实现服务器端支持分片下载</span></span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> <span class="comment">//获取文件总长度,用于分片</span></span><br><span class="line"> <span class="keyword">long</span> fSize = file.length();</span><br><span class="line"> <span class="comment">//设置内容类型,下载</span></span><br><span class="line"> response.setContentType(<span class="string">"application/x-download"</span>);</span><br><span class="line"> <span class="comment">//下载提示框显示正确的名字</span></span><br><span class="line"> String fileName = URLEncoder.encode(file.getName(),UTF8);</span><br><span class="line"> <span class="comment">//下载提示框</span></span><br><span class="line"> response.addHeader(<span class="string">"Content-Disposition"</span>,<span class="string">"attachment;filename="</span> + fileName);</span><br><span class="line"> <span class="comment">//告诉前端 支持分片下载</span></span><br><span class="line"> response.setHeader(<span class="string">"Accept-Range"</span>,<span class="string">"bytes"</span>);</span><br><span class="line"> <span class="comment">//总长度</span></span><br><span class="line"> response.setHeader(<span class="string">"fSize"</span>,String.valueOf(fSize));</span><br><span class="line"> <span class="comment">//文件名称</span></span><br><span class="line"> response.setHeader(<span class="string">"fName"</span>,fileName);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//进行分片操作</span></span><br><span class="line"> <span class="comment">//文件起始,文件已经读取了多少</span></span><br><span class="line"> <span class="keyword">long</span> pos = <span class="number">0</span>,last = fSize-<span class="number">1</span>,sum = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">//判断前端传过来的request是否支持分片下载</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> != request.getHeader(<span class="string">"Range"</span>)){</span><br><span class="line"> <span class="comment">//支持断点续传</span></span><br><span class="line"> response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取该分片起始位置</span></span><br><span class="line"> String numRange = request.getHeader(<span class="string">"Range"</span>).replaceAll(<span class="string">"bytes="</span>,<span class="string">""</span>);</span><br><span class="line"> String[] strRange = numRange.split(<span class="string">"-"</span>);</span><br><span class="line"> <span class="comment">//http Range bytes=100-1000 bytes=100-尾</span></span><br><span class="line"> <span class="comment">//等于2,表明有确定的起始位置</span></span><br><span class="line"> <span class="keyword">if</span>(strRange.length == <span class="number">2</span>){</span><br><span class="line"> pos = Long.parseLong(strRange[<span class="number">0</span>].trim());</span><br><span class="line"> last = Long.parseLong(strRange[<span class="number">1</span>].trim());</span><br><span class="line"> <span class="keyword">if</span>(last > fSize-<span class="number">1</span>){</span><br><span class="line"> last = fSize-<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//不等于2,表明只有确定的起点,是分片的最后一段</span></span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> pos = Long.parseLong(numRange.replaceAll(<span class="string">"-"</span>,<span class="string">""</span>).trim());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//该分片的大小</span></span><br><span class="line"> <span class="keyword">long</span> rangeLenght = last - pos +<span class="number">1</span>;</span><br><span class="line"> String contentRange = <span class="string">"bytes "</span> + pos + <span class="string">"-"</span> + last + <span class="string">"/"</span> + fSize;</span><br><span class="line"> <span class="comment">//响应分片的起始及大小</span></span><br><span class="line"> response.setHeader(<span class="string">"Content-Range"</span>,contentRange);</span><br><span class="line"> <span class="comment">//响应分片长度</span></span><br><span class="line"> response.setHeader(<span class="string">"Content-Lenght"</span>,String.valueOf(rangeLenght));</span><br><span class="line"></span><br><span class="line"> <span class="comment">//输入输出数据</span></span><br><span class="line"> os = <span class="keyword">new</span> BufferedOutputStream(response.getOutputStream());</span><br><span class="line"> is = <span class="keyword">new</span> BufferedInputStream(<span class="keyword">new</span> FileInputStream(file));</span><br><span class="line"> <span class="comment">//跳过前面已经读取过的分片</span></span><br><span class="line"> is.skip(pos);</span><br><span class="line"> <span class="keyword">byte</span>[] buffer = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</span><br><span class="line"> <span class="keyword">int</span> lenght = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">//当前分片的总数据量还未传输完</span></span><br><span class="line"> <span class="keyword">while</span>(sum < rangeLenght){</span><br><span class="line"> <span class="comment">//读取的起始位置,剩余长度小于等于buffer则取自身,否则取buffer的长度</span></span><br><span class="line"> lenght = is.read(buffer,<span class="number">0</span>,((rangeLenght-sum) <= buffer.length ? ((<span class="keyword">int</span>)(rangeLenght-sum)) : buffer.length));</span><br><span class="line"> <span class="comment">//计算已经输出的总数据量</span></span><br><span class="line"> sum = sum+ lenght;</span><br><span class="line"> <span class="comment">//输出数据</span></span><br><span class="line"> os.write(buffer,<span class="number">0</span>,lenght);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"下载完成"</span>);</span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span>(is != <span class="keyword">null</span>){</span><br><span class="line"> is.close();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(os != <span class="keyword">null</span>){</span><br><span class="line"> os.close();</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="客户端代码"><a href="#客户端代码" class="headerlink" title="客户端代码"></a>客户端代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 客户端分片下载</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: zh'b</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span>: 2021/10/6 20:34</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DownloadClient</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分片大小</span></span><br><span class="line"><span class="comment"> * 50MB</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="keyword">long</span> PER_PAGE = <span class="number">1024L</span> * <span class="number">1024L</span> * <span class="number">50L</span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 保存路径</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String DOWNPATH = <span class="string">"D:\\upload_test\\mytest"</span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分片下载用的线程池,建议自己手动创建</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> ExecutorService pool = Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 客户端支持分片下载</span></span><br><span class="line"><span class="comment"> * 下载示例:http://localhost:8080/downloadFile?downloadFileName=JWT视频.mp4</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> downloadFileName</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@RequestMapping("/downloadFile")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">downloadFile</span><span class="params">(String downloadFileName)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">//下载文件名为空</span></span><br><span class="line"> <span class="keyword">if</span>(downloadFileName==<span class="keyword">null</span>||<span class="string">""</span>.equals(downloadFileName)){</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"error"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//探测,试探性下载</span></span><br><span class="line"> FileInfo fileInfo = download( <span class="number">0</span>, <span class="number">10</span>, -<span class="number">1</span>, downloadFileName,<span class="keyword">null</span>);</span><br><span class="line"> <span class="comment">//总分片数量</span></span><br><span class="line"> <span class="keyword">long</span> pages = fileInfo.fSize / PER_PAGE;</span><br><span class="line"> <span class="comment">//进行分片</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">long</span> i=<span class="number">0</span>;i<=pages; i++){</span><br><span class="line"> <span class="comment">//将请求提交到线程池,实现异步</span></span><br><span class="line"> pool.submit(<span class="keyword">new</span> Download(i*PER_PAGE,(i+<span class="number">1</span>)*PER_PAGE-<span class="number">1</span>,i,downloadFileName,fileInfo.fName));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"success"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 储存分片信息</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@AllArgsConstructor</span></span><br><span class="line"> <span class="meta">@NoArgsConstructor</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">FileInfo</span></span>{</span><br><span class="line"> <span class="comment">//分片大小</span></span><br><span class="line"> <span class="keyword">long</span> fSize;</span><br><span class="line"> <span class="comment">//分片名</span></span><br><span class="line"> String fName;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用于实现分片的异步下载</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@AllArgsConstructor</span></span><br><span class="line"> <span class="meta">@NoArgsConstructor</span></span><br><span class="line"> <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Download</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span>{</span><br><span class="line"> <span class="comment">//分片的头</span></span><br><span class="line"> <span class="keyword">long</span> start;</span><br><span class="line"> <span class="comment">//分片的尾</span></span><br><span class="line"> <span class="keyword">long</span> end;</span><br><span class="line"> <span class="comment">//第几个分片</span></span><br><span class="line"> <span class="keyword">long</span> page;</span><br><span class="line"> <span class="comment">//下载的文件名</span></span><br><span class="line"> String downloadFileName;</span><br><span class="line"> <span class="comment">//文件名</span></span><br><span class="line"> String fName;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span 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>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> FileInfo info = download( start, end, page, downloadFileName,fName);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 客户端实现分片下载</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> start</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> page</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> downloadFileName</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> fName</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> FileInfo <span class="title">download</span><span class="params">(<span class="keyword">long</span> start,<span class="keyword">long</span> end,<span class="keyword">long</span> page,String downloadFileName,String fName)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">//断点下载</span></span><br><span class="line"> File file = <span class="keyword">new</span> File(DOWNPATH,page+<span class="string">"-"</span>+fName);</span><br><span class="line"> <span class="comment">//不是探测文件,并且文件已经下载完成,并且文件完整</span></span><br><span class="line"> <span class="keyword">if</span>(page!=-<span class="number">1</span> && file.exists() && file.length()==PER_PAGE){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//实现分片下载</span></span><br><span class="line"> HttpClient client = HttpClients.createDefault();</span><br><span class="line"> <span class="comment">//原下载路径</span></span><br><span class="line"> HttpGet httpGet = <span class="keyword">new</span> HttpGet(<span class="string">"http://127.0.0.1:8080/download?downloadFileName="</span>+downloadFileName);</span><br><span class="line"> <span class="comment">//设置分片</span></span><br><span class="line"> httpGet.setHeader(<span class="string">"Range"</span>,<span class="string">"bytes="</span>+start+<span class="string">"-"</span>+end);</span><br><span class="line"> <span class="comment">//发送请求</span></span><br><span class="line"> HttpResponse response = client.execute(httpGet);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取文件流</span></span><br><span class="line"> HttpEntity entity = response.getEntity();</span><br><span class="line"> InputStream is = entity.getContent();</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取分片大小</span></span><br><span class="line"> String fSize = response.getFirstHeader(<span class="string">"fSize"</span>).getValue();</span><br><span class="line"> <span class="comment">//获取分片名</span></span><br><span class="line"> fName = URLDecoder.decode(response.getFirstHeader(<span class="string">"fName"</span>).getValue(),<span class="string">"utf-8"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//传输数据</span></span><br><span class="line"> FileOutputStream fis = <span class="keyword">new</span> FileOutputStream(file);</span><br><span class="line"> <span class="keyword">byte</span>[] buffer = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</span><br><span class="line"> <span class="keyword">int</span> ch =<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>((ch = is.read(buffer)) != -<span class="number">1</span>){</span><br><span class="line"> fis.write(buffer,<span class="number">0</span>,ch);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//关闭传输流</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> is.close();</span><br><span class="line"> fis.flush();</span><br><span class="line"> fis.close();</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//判断是否为最后一个分片</span></span><br><span class="line"> <span class="keyword">if</span>(end - Long.parseLong(fSize) >= <span class="number">0</span>){</span><br><span class="line"> mergeFile(fName,page);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> FileInfo(Long.parseLong(fSize), fName);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 合并下载完成的分片文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> fName</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> page</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">mergeFile</span><span class="params">(String fName, <span class="keyword">long</span> page)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">//最终文件</span></span><br><span class="line"> File tempFile = <span class="keyword">new</span> File(DOWNPATH,fName);</span><br><span class="line"> <span class="comment">//获取相应的输出流</span></span><br><span class="line"> BufferedOutputStream os = <span class="keyword">new</span> BufferedOutputStream(<span class="keyword">new</span> FileOutputStream(tempFile));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span> ;i<=page;i++){</span><br><span class="line"> <span class="comment">//寻找分片文件</span></span><br><span class="line"> File file = <span class="keyword">new</span> File(DOWNPATH,i+<span class="string">"-"</span>+fName);</span><br><span class="line"> <span class="comment">//可能某一分片还未下载完成</span></span><br><span class="line"> <span class="keyword">while</span>(!file.exists() || (i != page && file.length() < PER_PAGE)){</span><br><span class="line"> Thread.sleep(<span class="number">100</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">byte</span>[] bytes = FileUtils.readFileToByteArray(file);</span><br><span class="line"> <span class="comment">//输出到总文件后面</span></span><br><span class="line"> os.write(bytes);</span><br><span class="line"> os.flush();</span><br><span class="line"> <span class="comment">//删除临时文件</span></span><br><span class="line"> file.delete();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//删除探测文件</span></span><br><span class="line"> File file = <span class="keyword">new</span> File(DOWNPATH,-<span class="number">1</span>+<span class="string">"-null"</span>);</span><br><span class="line"> file.delete();</span><br><span class="line"> os.flush();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> os.close();</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="提示"><a href="#提示" class="headerlink" title="提示"></a>提示</h3><p><code>如果只实现了服务器代码,未实现客户端代码 则与 普通下载无任何差异,在此就不进行演示</code></p><p>如果要实现客户端的分片下载,需同时实现上面两段代码,因为<code>客户端代码需去请求服务器端代码,来实现分片下载,请注意客户端代码的第110行</code></p><p>在请求时使用客户端代码的请求方式就能实现客户端的分片下载,请看一下示例</p><h3 id="示例-1"><a href="#示例-1" class="headerlink" title="示例"></a>示例</h3><p><img src="https://i.loli.net/2021/10/07/u5EANJnOdGI8HeK.png" alt="image-20211007103307366"></p><p><img src="https://i.loli.net/2021/10/07/yS57hxVtzJjEkGH.png" alt="image-20211007103422944"></p><p><img src="https://i.loli.net/2021/10/07/93VylEdOTfgQ8jr.png" alt="image-20211007103441408"></p><p><img src="https://i.loli.net/2021/10/07/8GBRhWusDyNgS6m.png" alt="image-20211007103729374"></p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 上传下载 </tag>
</tags>
</entry>
<entry>
<title>简单文件上传与下载</title>
<link href="/2021/10/07/%E7%AE%80%E5%8D%95%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E4%B8%8E%E4%B8%8B%E8%BD%BD/"/>
<url>/2021/10/07/%E7%AE%80%E5%8D%95%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E4%B8%8E%E4%B8%8B%E8%BD%BD/</url>
<content type="html"><![CDATA[<h1 id="简单文件上传与下载"><a href="#简单文件上传与下载" class="headerlink" title="简单文件上传与下载"></a>简单文件上传与下载</h1><h2 id="上传"><a href="#上传" class="headerlink" title="上传"></a>上传</h2><h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"> <span class="attr">servlet:</span></span><br><span class="line"> <span class="attr">multipart:</span></span><br><span class="line"> <span class="attr">max-file-size:</span> <span class="string">300MB</span></span><br><span class="line"> <span class="attr">max-request-size:</span> <span class="string">300MB</span></span><br></pre></td></tr></table></figure><h3 id="前端代码"><a href="#前端代码" class="headerlink" title="前端代码"></a>前端代码</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- class不用管!!! --></span></span><br><span class="line"><span class="tag"><<span class="name">form</span> <span class="attr">action</span>=<span class="string">"/admin/upload"</span> <span class="attr">method</span>=<span class="string">"post"</span> <span class="attr">enctype</span>=<span class="string">"multipart/form-data"</span> <span class="attr">class</span>=<span class="string">"ui form"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"required field"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"ui left labeled input"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"file"</span> <span class="attr">name</span>=<span class="string">"file"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"ui right aligned container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">class</span>=<span class="string">"ui teal submit button"</span>></span>提交<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">class</span>=<span class="string">"ui button"</span> <span class="attr">onclick</span>=<span class="string">"window.history.go(-1)"</span>></span>返回<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><h3 id="后端代码"><a href="#后端代码" class="headerlink" title="后端代码"></a>后端代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UploadController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//上传后文件保存位置</span></span><br><span class="line"> <span class="meta">@Value("static/files")</span></span><br><span class="line"> <span class="keyword">private</span> String fileLocation;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@PostMapping("/admin/upload")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">upload</span><span class="params">(MultipartFile file, Model model)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"进入upload"</span>);</span><br><span class="line"> <span class="comment">// 获得 classpath 的绝对路径</span></span><br><span class="line"> String realPath = ResourceUtils.getURL(<span class="string">"classpath:"</span>).getPath() + fileLocation;</span><br><span class="line"> File newFile = <span class="keyword">new</span> File(realPath);</span><br><span class="line"> <span class="comment">// 如果文件夹不存在、则新建</span></span><br><span class="line"> <span class="keyword">if</span> (!newFile.exists()){</span><br><span class="line"> newFile.mkdirs();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 上传</span></span><br><span class="line"> Date date = <span class="keyword">new</span> Date();</span><br><span class="line"> String fileName = date.getTime() +<span class="string">"@"</span> + file.getOriginalFilename();</span><br><span class="line"> file.transferTo(<span class="keyword">new</span> File(newFile, fileName));</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"admin/upload"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><p><img src="https://i.loli.net/2021/10/06/DL69bNUP3ovTARi.png" alt="image-20211006143604933"></p><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><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UploadController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Value("static/files")</span></span><br><span class="line"> <span class="keyword">private</span> String fileLocation;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@GetMapping("/admin/download")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">download</span><span class="params">(String fileName, HttpServletResponse response)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> System.out.println(<span class="string">"download"</span>);</span><br><span class="line"> <span class="comment">// 获得待下载文件所在文件夹的绝对路径</span></span><br><span class="line"> String realPath = ResourceUtils.getURL(<span class="string">"classpath:"</span>).getPath() + fileLocation;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 获得文件输入流</span></span><br><span class="line"> FileInputStream inputStream = <span class="keyword">new</span> FileInputStream(<span class="keyword">new</span> File(realPath, fileName));</span><br><span class="line"> <span class="comment">// 设置响应头、以附件形式打开文件</span></span><br><span class="line"> response.setHeader(<span class="string">"content-disposition"</span>, <span class="string">"attachment; fileName="</span> + fileName);</span><br><span class="line"> ServletOutputStream outputStream = response.getOutputStream();</span><br><span class="line"> <span class="keyword">int</span> len = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">byte</span>[] data = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</span><br><span class="line"> <span class="keyword">while</span> ((len = inputStream.read(data)) != -<span class="number">1</span>) {</span><br><span class="line"> outputStream.write(data, <span class="number">0</span>, len);</span><br><span class="line"> }</span><br><span class="line"> outputStream.close();</span><br><span class="line"> inputStream.close();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="请求方式"><a href="#请求方式" class="headerlink" title="请求方式"></a>请求方式</h3><p><code>http://localhost:8080/admin/[email protected]</code></p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 上传下载 </tag>
</tags>
</entry>
<entry>
<title>平衡二叉树调整</title>
<link href="/2021/09/29/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91%E8%B0%83%E6%95%B4/"/>
<url>/2021/09/29/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91%E8%B0%83%E6%95%B4/</url>
<content type="html"><![CDATA[<h1 id="平衡二叉树调整"><a href="#平衡二叉树调整" class="headerlink" title="平衡二叉树调整"></a>平衡二叉树调整</h1><p>参考视频:<a href="https://www.bilibili.com/video/BV1Ro4y1S7wb?from=search&seid=1347340633041499968&spm_id_from=333.337.0.0">简单粗暴的方式解决平衡二叉树的调整_哔哩哔哩_bilibili</a></p><h2 id="LL型"><a href="#LL型" class="headerlink" title="LL型"></a>LL型</h2><p>右旋,右孩子变左孩子</p><p><img src="https://i.loli.net/2021/09/29/mhDLyVX7zE4vF6B.png" alt="image-20210929115632431"></p><h2 id="RR型"><a href="#RR型" class="headerlink" title="RR型"></a>RR型</h2><p>左旋,左孩子变右孩子</p><p><img src="https://i.loli.net/2021/09/29/1OtwNrz6KmTUIc5.png" alt="image-20210929115731480"></p><h2 id="LR型"><a href="#LR型" class="headerlink" title="LR型"></a>LR型</h2><p>黄色节点成根节点,其左孩子变右孩子,右孩子变左孩子</p><p><img src="https://i.loli.net/2021/09/29/4YZyI6XS7Kzuahk.png" alt="image-20210929115821162"></p><h2 id="RL型"><a href="#RL型" class="headerlink" title="RL型"></a>RL型</h2><p>黄色节点成根节点,其左孩子变右孩子,右孩子变左孩子</p><p><img src="https://i.loli.net/2021/09/29/D1Two9EXbYpPzN2.png" alt="image-20210929120212360"></p><h2 id="确定类型"><a href="#确定类型" class="headerlink" title="确定类型"></a>确定类型</h2><p>由不平衡的节点向新插入的节点遍历,取前3个结点连起来的形状</p><p>如LL、RR、LR、RL</p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>首先确定类型是LL、RR、LR、RL中的那种</p><p>如果是LL或RR,先右旋或左旋,再把变换前第2个结点(变换后的根节点)的子节点的位置</p><p>如果是LL,因为要右旋,则把其右孩子变为第1个结点(变换前的根节点)的左孩子</p><p>如果是RR,因为要左旋,则把其左孩子变为第1个结点(变换前的根节点)的右孩子</p><p>如果是LR或RL,直接将第3个结点变为根节点,即将3个结点中最下方或处于中间那个结点作为根节点</p><p>把它的左孩子变为右孩子,右孩子变为左孩子,</p><p>即左孩子去它(变换后为根节点)的左子树做右孩子。右孩子去它(变换后为根节点)的右子树做左孩子</p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 树 </tag>
</tags>
</entry>
<entry>
<title>Redis:5大基本数据类型底层实现(初探)</title>
<link href="/2021/09/16/Redis%EF%BC%9A5%E5%A4%A7%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%EF%BC%88%E5%88%9D%E6%8E%A2%EF%BC%89/"/>
<url>/2021/09/16/Redis%EF%BC%9A5%E5%A4%A7%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%EF%BC%88%E5%88%9D%E6%8E%A2%EF%BC%89/</url>
<content type="html"><![CDATA[<h1 id="Redis:5大基本数据类型底层实现(初探)"><a href="#Redis:5大基本数据类型底层实现(初探)" class="headerlink" title="Redis:5大基本数据类型底层实现(初探)"></a>Redis:5大基本数据类型底层实现(初探)</h1><p>参考文章:<a href="https://xie.infoq.cn/article/98c984f6462aec99ffc0c3b42">Redis系列(二): 连集合底层实现原理都不知道,你敢说Redis用的很溜? - InfoQ 写作平台</a></p><p><a href="https://blog.csdn.net/weixin_41519463/article/details/109208476">redis的5种数据结构及其底层实现原理_zhongzhh8的博客-CSDN博客_redis数据结构的底层实现</a></p><h2 id="String"><a href="#String" class="headerlink" title="String"></a>String</h2><p><code>SDS</code>:全称 <code>Simple Dynamic String</code>,即简单动态字符串</p><h3 id="SDS"><a href="#SDS" class="headerlink" title="SDS"></a>SDS</h3><ul><li>SDS:<ul><li>组成部分<ul><li><strong>free:表示 buf 中的空闲的空间大小</strong></li><li><strong>len:表示 buf 中的内容长度</strong></li><li><strong>buf:一个 char 类型的数组,用于存储实际字符串的内容。</strong></li></ul></li><li>优点<ul><li><strong>获取字符串长度的复杂度为 O(1)</strong></li><li><strong>防止 buf 存储内容溢出的问题</strong></li><li><strong>空间预分配 &空间惰性释放</strong></li><li><strong>二级制安全性</strong></li></ul></li></ul></li></ul><h2 id="List"><a href="#List" class="headerlink" title="List"></a>List</h2><p>redis3.2之前由 <code>ZipList</code> 和 <code>LinkedList</code> 实现,3.2之后由 <code>QuickList</code>实现</p><h3 id="ZipList"><a href="#ZipList" class="headerlink" title="ZipList"></a>ZipList</h3><ul><li>ZipList:由一块连续的存储空间组成<ul><li>使用条件<ul><li><strong>List 中存储的每个元素的长度小于 64byte</strong></li><li><strong>元素个数小于 512</strong></li></ul></li><li>组成部分<ul><li><strong>zlbytes:表示当前 list 的存储元素的总长度。</strong></li><li><strong>zllen:表示当前 list 存储的元素的个数。</strong></li><li><strong>zltail:表示当前 list 的头结点的地址,通过 zltail 就是可以实现 list 的遍历。</strong></li><li><strong>zlend:表示当前 list 的结束标识。</strong></li><li><strong>entry:表示存储实际数据的节点,每个 entry 代表一个元素。</strong></li><li><strong>previours_entry_length: 表示当前节点元素的长度,通过其长度可以计算出下一个元素的位置。</strong></li><li><strong>encoding:表示元素的编码格式。</strong></li><li><strong>content:表示实际存储的元素内容。</strong></li></ul></li></ul></li></ul><h3 id="LinkedList"><a href="#LinkedList" class="headerlink" title="LinkedList"></a>LinkedList</h3><ul><li>LinkedList:由不连续的内存块形成的双向链表组成<ul><li>组成部分<ul><li><strong>head:表示 List 的头结点;通过其可以找到 List 的头节点。</strong></li><li><strong>tail:表示 List 的尾节点;通过其可以找到 List 的尾节点。</strong></li><li><strong>len:表示 List 存储的元素个数。</strong></li><li><strong>dup:表示用于复制元素的函数。</strong></li><li><strong>free:表示用于释放元素的函数。</strong></li><li><strong>match:表示用于对比元素的函数。</strong></li></ul></li></ul></li></ul><h3 id="QuickList"><a href="#QuickList" class="headerlink" title="QuickList"></a>QuickList</h3><ul><li>QuickList:结合ZipList和LinkedList<ul><li>组成部分<ul><li>整体上采用LinkedList</li><li>具体每个结点采用ZipList</li></ul></li></ul></li></ul><h2 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h2><p>Set集合由Intset和字典组成</p><h3 id="Insert"><a href="#Insert" class="headerlink" title="Insert"></a>Insert</h3><ul><li>Intset<ul><li>使用条件<ul><li><strong>元素均为整数</strong></li><li><strong>元素个数小于 512</strong></li></ul></li></ul></li></ul><h3 id="字典"><a href="#字典" class="headerlink" title="字典"></a>字典</h3><ul><li>字典</li></ul><h2 id="Zset"><a href="#Zset" class="headerlink" title="Zset"></a>Zset</h2><p>Zset由ZipList 和 SkipList组成,每个元素包含数据本身和一个对应的分数(score)</p><h3 id="ZipList-1"><a href="#ZipList-1" class="headerlink" title="ZipList"></a>ZipList</h3><ul><li>ZipList </li></ul><h3 id="SkipList"><a href="#SkipList" class="headerlink" title="SkipList"></a>SkipList</h3><ul><li> SkipList</li><li>组成部分<ul><li>dict<ul><li>字典</li></ul></li><li>zset <ul><li>跳表</li></ul></li></ul></li></ul><h2 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h2><p>Hash由ZipList 和HashTable 组成</p><h3 id="ZipList-2"><a href="#ZipList-2" class="headerlink" title="ZipList"></a>ZipList</h3><ul><li>ZipList </li></ul><h3 id="HashTable"><a href="#HashTable" class="headerlink" title="HashTable"></a>HashTable</h3><ul><li>HashTable</li></ul>]]></content>
<categories>
<category> Redis </category>
</categories>
<tags>
<tag> redis </tag>
</tags>
</entry>
<entry>
<title>JWT小结</title>
<link href="/2021/08/24/JWT%E5%B0%8F%E7%BB%93/"/>
<url>/2021/08/24/JWT%E5%B0%8F%E7%BB%93/</url>
<content type="html"><![CDATA[<h1 id="JWT小结"><a href="#JWT小结" class="headerlink" title="JWT小结"></a>JWT小结</h1><p>参考文章:<a href="https://www.jianshu.com/p/576dbf44b2ae">什么是 JWT – JSON WEB TOKEN - 简书 (jianshu.com)</a></p><h2 id="什么是JWT"><a href="#什么是JWT" class="headerlink" title="什么是JWT"></a>什么是JWT</h2><blockquote><p>JSON WEB TOKEN (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(<a href="https://link.jianshu.com/?t=https://tools.ietf.org/html/rfc7519">(RFC 7519</a>).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。</p></blockquote><h2 id="token的构成"><a href="#token的构成" class="headerlink" title="token的构成"></a>token的构成</h2><p>token由<code>头部</code>、<code>载荷</code>、<code>签名</code>三部分组成</p><h3 id="头部(header)"><a href="#头部(header)" class="headerlink" title="头部(header)"></a>头部(header)</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> 'typ': 'JWT',<span class="comment">//声明类型</span></span><br><span class="line"> 'alg': 'HS256'<span class="comment">//加密算法</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//将头部进行base64加密后得到第一部分</span></span><br><span class="line">eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9</span><br></pre></td></tr></table></figure><h3 id="载荷(playload)"><a href="#载荷(playload)" class="headerlink" title="载荷(playload)"></a>载荷(playload)</h3><p>载荷用于存放要保存的信息,如用户id、权限、<strong>过期时间</strong>等。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"sub"</span>: <span class="string">"1234567890"</span>,</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"John Doe"</span>,</span><br><span class="line"> <span class="attr">"admin"</span>: <span class="literal">true</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//将载荷进行base64加密后得到第二部分</span></span><br><span class="line">eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9</span><br></pre></td></tr></table></figure><h3 id="签名(signature)"><a href="#签名(signature)" class="headerlink" title="签名(signature)"></a>签名(signature)</h3><p>由三部分组成:加密后的头部header,加密后的载荷playload,加盐secret</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// javascript</span></span><br><span class="line"><span class="keyword">var</span> encodedString = base64UrlEncode(header) + <span class="string">'.'</span> + base64UrlEncode(payload);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> signature = HMACSHA256(encodedString, <span class="string">'secret'</span>); </span><br><span class="line"></span><br><span class="line"><span class="comment">//将加密后的头部和载荷结合起来,使用header中的加密算法进行加盐secret组合加密,得到第三部分</span></span><br><span class="line">TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ</span><br></pre></td></tr></table></figure><h3 id="完整的token"><a href="#完整的token" class="headerlink" title="完整的token"></a>完整的token</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ</span><br></pre></td></tr></table></figure><h2 id="如何验证token是否有效"><a href="#如何验证token是否有效" class="headerlink" title="如何验证token是否有效"></a>如何验证token是否有效</h2><p>获取token的前两部分,即加密后的头部和载荷</p><ul><li>检查token是否过期:将载荷解密后检查过期时间</li><li>验证token是否未被篡改:再次使用加密后的头部和载荷生成签名,比较两次签名是否相同</li></ul><h2 id="token由谁颁发"><a href="#token由谁颁发" class="headerlink" title="token由谁颁发"></a>token由谁颁发</h2><p>用户在第一次登录后,用认证中心统一颁发token</p><h2 id="token如何传输"><a href="#token如何传输" class="headerlink" title="token如何传输"></a>token如何传输</h2><p>客户端在进行请求的时候,将token存放在cookie中或者存放在请求头中</p><h2 id="token续签"><a href="#token续签" class="headerlink" title="token续签"></a>token续签</h2><h3 id="使用redis缓存"><a href="#使用redis缓存" class="headerlink" title="使用redis缓存"></a>使用redis缓存</h3><p>认证中心在颁发token的同时,在redis设置key为<code>用户数据(用户id或其他特征信息)+客户端环境特征(IP、UA、网卡MAC等),最好再将key进行加密,如MD5</code>,并设置对应的过期时间。</p><p>当收到token时,检查在redis是否过期:</p><ol><li>如果过期则要求用户重新登录,</li><li>否则检查时间是否过半,如果没有则按照正常流程执行,</li><li>否则在redis中为key增加一半的有效时间</li></ol><h3 id="使用两个token"><a href="#使用两个token" class="headerlink" title="使用两个token"></a>使用两个token</h3><p>认证中心在颁发token的时候,颁发两个token,一个为正常使用token,一个为续签token,续签token过期时间比正常使用token时间长一倍。</p><ol><li>当正常使用token未过期则按正常流程执行;</li><li>当正常使用token过期,续签token未过期,则重新签发两个新的token,延长其过期时间外其他信息完全相同;</li><li>如果续签token过期,则要求用户重新登录。</li></ol><p><strong>存在重复生成JWT的问题,解决办法:在认证中心设计一个Map,记录过去一段时间内(几秒钟)的原始JWT(key)和新生成的JWT(value),如果在一段时间发现同样的原始JWT,则返回相同的新JWT。</strong></p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 网络 </tag>
</tags>
</entry>
<entry>
<title>HTTPS和SSL</title>
<link href="/2021/08/24/HTTPS%E5%92%8CSSL/"/>
<url>/2021/08/24/HTTPS%E5%92%8CSSL/</url>
<content type="html"><![CDATA[<h1 id="HTTPS和SSL"><a href="#HTTPS和SSL" class="headerlink" title="HTTPS和SSL"></a>HTTPS和SSL</h1><p>参考文章:<a href="https://blog.csdn.net/qq_40149612/article/details/82795656">SSL及其加密通信过程_Jxhacker的博客-CSDN博客_ssl加密过程</a>,<a href="https://blog.csdn.net/qq_38265137/article/details/90112705">SSL协议原理详解_曹世宏的博客-CSDN博客_ssl协议</a></p><h2 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h2><p>HTTP(Hyper TEXT Transfer Protocol超文本传输协议)是目前互联网上应用最为广泛的一种网络协议,用于在Web浏览器和网站服务器之间传递信息,但是HTTP协议以明文的方式发送内容,不提供任何数据加密,攻击者能够很轻易通过抓包的方式截取传输内容并读懂其中的信息,所以HTTP不适合传输一些比较私密的信息,为了解决HTTP这一缺陷,HTTPS(Hyper TEXT Transfer Protocol over Secure Socket Layer)协议出现。HTTPS是在HTTP的基础上加入SSL协议。传输以密文传输,保证数据传输的安全以及确认网站的真实性(数字证书)。</p><h2 id="SSL"><a href="#SSL" class="headerlink" title="SSL"></a>SSL</h2><p>SSL英文全称Secure Socket Layer,安全套接层,是一种为网络通信提供安全以及数据完整性的安全协议,它在传输层对网络进行加密。它主要是分为两层:</p><ul><li>SSL记录协议:为高层协议提供安全封装、压缩、加密等基本功能</li><li>SSL握手协议:用于在数据传输开始前进行通信双方的身份验证、加密算法的协商、交换密钥<br>OpenSSL是SSL的开源实现,它是作为密码学的安全开发包,提供相当强大全面的功能,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议,并提供了丰富的应用程序供测试或其它目的使用。它一共提供了8中对称加密算法和4中非对称加密算法。</li></ul><h2 id="SSL加密过程"><a href="#SSL加密过程" class="headerlink" title="SSL加密过程"></a>SSL加密过程</h2><ol><li>第一步:客户端A给出<code>客户端版本</code>+<code>一个随机数</code>+<code>自己支持的加密方式</code></li><li>第二步:服务器B返回<code>从客户端支持的加密方式中选择一种</code>+<code>一个随机数</code>+<code>安全证书</code></li><li>第三步:客户端A验证证书的有效性(以下是验证证书的步骤),然后再生成<code>一个随机数</code>,并使用证书中的公钥加密,发送给服务器B<ul><li>客户端通过浏览器内置的CA根证书,获取对应CA机构的公钥</li><li>服务器B返回自己的安全证书,客户端使用CA公钥解密被CA私钥加密的信息得到一个hash值,再使用证书中的hash算法生成hash值,比较两个hash值</li></ul></li><li>第四步:服务器B使用私钥解密这个随机数</li><li>第五步:通过第二步确认的加密方式和第一、二、三步生成的3个随机数生成一个对话秘钥,用于通信。</li></ol><p><strong>小结:HTTPS在验证证书阶段使用的是非对称加密(更安全),在数据传输阶段使用对称加密(更高效)。</strong></p>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 网络 </tag>
</tags>
</entry>
<entry>
<title>01背包和完全背包</title>
<link href="/2021/08/23/01%E8%83%8C%E5%8C%85%E5%92%8C%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85/"/>
<url>/2021/08/23/01%E8%83%8C%E5%8C%85%E5%92%8C%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85/</url>
<content type="html"><![CDATA[<h1 id="01背包和完全背包"><a href="#01背包和完全背包" class="headerlink" title="01背包和完全背包"></a>01背包和完全背包</h1><h2 id="01背包"><a href="#01背包" class="headerlink" title="01背包"></a>01背包</h2><h3 id="题目介绍"><a href="#题目介绍" class="headerlink" title="题目介绍"></a>题目介绍</h3><p><strong>有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,使装入的物品总价值最大</strong></p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><p>参考文章:<a href="https://www.acwing.com/solution/content/1374/">AcWing 2. 01背包问题(状态转移方程讲解) - AcWing</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Scanner;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span></span>{</span><br><span 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>{</span><br><span class="line"> Scanner s = <span class="keyword">new</span> Scanner(System.in);</span><br><span class="line"> <span class="keyword">int</span> N=s.nextInt();</span><br><span class="line"> <span class="keyword">int</span> V=s.nextInt();</span><br><span class="line"> <span class="keyword">int</span>[] v=<span class="keyword">new</span> <span class="keyword">int</span>[N+<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">int</span>[] w=<span class="keyword">new</span> <span class="keyword">int</span>[N+<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">1</span>;i<=N;i++){</span><br><span class="line"> v[i]=s.nextInt();</span><br><span class="line"> w[i]=s.nextInt();</span><br><span class="line"> }</span><br><span class="line"> s.close();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//二维数组解决</span></span><br><span class="line"> <span class="comment">// int[][] dp=new int[N+1][V+1];</span></span><br><span class="line"> <span class="comment">// for (int i = 1; i <= N; i++) {</span></span><br><span class="line"> <span class="comment">// for (int j = 0; j <= V; j++) {</span></span><br><span class="line"> <span class="comment">// if (j>=v[i]){</span></span><br><span class="line"> <span class="comment">// dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);</span></span><br><span class="line"> <span class="comment">// }else {</span></span><br><span class="line"> <span class="comment">// dp[i][j]=dp[i-1][j];</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// System.out.println(dp[N][V]);</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//一维数组解决</span></span><br><span class="line"> <span class="keyword">int</span>[] dp=<span class="keyword">new</span> <span class="keyword">int</span>[V+<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">1</span>;i<=N;i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j=V;j>=v[i];j--){</span><br><span class="line"> dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> System.out.println(dp[V]);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="完全背包"><a href="#完全背包" class="headerlink" title="完全背包"></a>完全背包</h2><h3 id="题目介绍-1"><a href="#题目介绍-1" class="headerlink" title="题目介绍"></a>题目介绍</h3><p><strong>有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且都有无限件可用,要求在有限的背包容量下,使装入的物品总价值最大</strong></p><h3 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h3><p>参考文章:<a href="https://www.acwing.com/solution/content/5345/">AcWing 3. 完全背包问题 - AcWing</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Scanner;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span></span>{</span><br><span 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 class="keyword">throws</span> Exception</span>{</span><br><span class="line"> Scanner s=<span class="keyword">new</span> Scanner(System.in);</span><br><span class="line"> <span class="keyword">int</span> N=s.nextInt();</span><br><span class="line"> <span class="keyword">int</span> V=s.nextInt();</span><br><span class="line"> <span class="keyword">int</span>[] v=<span class="keyword">new</span> <span class="keyword">int</span>[N+<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">int</span>[] w=<span class="keyword">new</span> <span class="keyword">int</span>[N+<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">1</span>;i<=N;i++){</span><br><span class="line"> v[i]=s.nextInt();</span><br><span class="line"> w[i]=s.nextInt();</span><br><span class="line"> }</span><br><span class="line"> s.close();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//二维数组解决</span></span><br><span class="line"> <span class="comment">// int[][] dp=new int[N+1][V+1];</span></span><br><span class="line"> <span class="comment">// for(int i=1;i<=N;i++){</span></span><br><span class="line"> <span class="comment">// for(int j=0;j<=V;j++){</span></span><br><span class="line"> <span class="comment">// for(int k=0;k*v[i]<=j;k++){</span></span><br><span class="line"> <span class="comment">// dp[i][j]=Math.max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">// System.out.println(dp[N][V]);</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//一维数组解决</span></span><br><span class="line"> <span class="keyword">int</span>[] dp=<span class="keyword">new</span> <span class="keyword">int</span>[V+<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">1</span>;i<=N;i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j=v[i];j<=V;j++){</span><br><span class="line"> dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> System.out.println(dp[V]);</span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="背记"><a href="#背记" class="headerlink" title="背记"></a>背记</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] dp=<span class="keyword">new</span> <span class="keyword">int</span>[V+<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">//01背包</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">1</span>;i<=N;i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j=V;j>=v[i];j--){</span><br><span class="line"> dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">//完全背包</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">1</span>;i<=N;i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j=v[i];j<=V;j++){</span><br><span class="line"> dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">System.out.println(dp[V]);</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java面试 </category>
</categories>
<tags>
<tag> 刷题 </tag>
</tags>
</entry>
<entry>
<title>JDK1.8新特性</title>
<link href="/2021/08/23/JDK1-8%E6%96%B0%E7%89%B9%E6%80%A7/"/>
<url>/2021/08/23/JDK1-8%E6%96%B0%E7%89%B9%E6%80%A7/</url>
<content type="html"><![CDATA[<h1 id="JDK1-8新特性"><a href="#JDK1-8新特性" class="headerlink" title="JDK1.8新特性"></a>JDK1.8新特性</h1><ol><li>lambda简化</li><li>stream流</li><li>新的hashmap、concurrentHashmap</li><li>元空间</li><li>接口中允许有静态方法和默认方法</li><li>localdatetime</li><li>optional</li><li>CompletableFuture</li></ol>]]></content>
<categories>
<category> Java基础 </category>
</categories>
<tags>
<tag> 集合框架 </tag>
<tag> stream </tag>
</tags>
</entry>
<entry>
<title>ConcurrentHashMap部分源码简析</title>
<link href="/2021/08/17/ConcurrentHashMap%E9%83%A8%E5%88%86%E6%BA%90%E7%A0%81%E7%AE%80%E6%9E%90/"/>
<url>/2021/08/17/ConcurrentHashMap%E9%83%A8%E5%88%86%E6%BA%90%E7%A0%81%E7%AE%80%E6%9E%90/</url>
<content type="html"><![CDATA[<h1 id="ConcurrentHashMap部分源码简析"><a href="#ConcurrentHashMap部分源码简析" class="headerlink" title="ConcurrentHashMap部分源码简析"></a>ConcurrentHashMap部分源码简析</h1><p>本文参考:<a href="https://blog.csdn.net/jianghuxiaojin/article/details/52006118">java-并发-ConcurrentHashMap高并发机制-jdk1.8_阿里Darker-CSDN博客</a></p><h2 id="核心属性或方法"><a href="#核心属性或方法" class="headerlink" title="核心属性或方法"></a>核心属性或方法</h2><h3 id="table"><a href="#table" class="headerlink" title="table"></a>table</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The array of bins. Lazily initialized upon first insertion.</span></span><br><span class="line"><span class="comment"> * Size is always a power of two. Accessed directly by iterators.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//盛装Node元素的数组 它的大小是2的整数次幂</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">volatile</span> Node<K,V>[] table;</span><br></pre></td></tr></table></figure><h3 id="sizeCtl"><a href="#sizeCtl" class="headerlink" title="sizeCtl"></a>sizeCtl</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Table initialization and resizing control. When negative, the</span></span><br><span class="line"><span class="comment"> * table is being initialized or resized: -1 for initialization,</span></span><br><span class="line"><span class="comment"> * else -(1 + the number of active resizing threads). Otherwise,</span></span><br><span class="line"><span class="comment"> * when table is null, holds the initial table size to use upon</span></span><br><span class="line"><span class="comment"> * creation, or 0 for default. After initialization, holds the</span></span><br><span class="line"><span class="comment"> * next element count value upon which to resize the table.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//表初始化和调整大小控制。 </span></span><br><span class="line"><span class="comment">//如果为负,则表正在初始化或扩容:-1 表示初始化,否则表示有N-1个线程正在协助扩容。 </span></span><br><span class="line"><span class="comment">//0或正数代表table还未初始化,或下一次扩容的阈值</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="keyword">int</span> sizeCtl;</span><br></pre></td></tr></table></figure><h3 id="hashcode"><a href="#hashcode" class="headerlink" title="hashcode"></a>hashcode</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Encodings for Node hash fields. See above for explanation.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//表示这是一个ForwardingNode结点,表示在扩容时扩容线程跳过该结点(已被处理)</span></span><br><span 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></span><br><span class="line"><span class="comment">//表示这是一颗树</span></span><br><span 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></span><br><span 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></span><br><span 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></span><br></pre></td></tr></table></figure><h3 id="Node"><a href="#Node" class="headerlink" title="Node"></a>Node</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Key-value entry. This class is never exported out as a</span></span><br><span class="line"><span class="comment"> * user-mutable Map.Entry (i.e., one supporting setValue; see</span></span><br><span class="line"><span class="comment"> * MapEntry below), but can be used for read-only traversals used</span></span><br><span class="line"><span class="comment"> * in bulk tasks. Subclasses of Node with a negative hash field</span></span><br><span class="line"><span class="comment"> * are special, and contain null keys and values (but are never</span></span><br><span class="line"><span class="comment"> * exported). Otherwise, keys and vals are never null.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//ConcurrentHashMap1.8采用Node数组,结构与hashmap相似</span></span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span><<span class="title">K</span>,<span class="title">V</span>> <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span><<span class="title">K</span>,<span class="title">V</span>> </span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> hash;</span><br><span class="line"> <span class="keyword">final</span> K key;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//保证val和next的可见性</span></span><br><span class="line"> <span class="keyword">volatile</span> V val;</span><br><span class="line"> <span class="keyword">volatile</span> Node<K,V> next;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//构造方法</span></span><br><span class="line"> Node(<span class="keyword">int</span> hash, K key, V val, Node<K,V> next) {</span><br><span class="line"> <span class="keyword">this</span>.hash = hash;</span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> <span class="keyword">this</span>.val = val;</span><br><span class="line"> <span class="keyword">this</span>.next = next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> K <span class="title">getKey</span><span class="params">()</span> </span>{ <span class="keyword">return</span> key; }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">getValue</span><span class="params">()</span> </span>{ <span class="keyword">return</span> val; }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>{ <span class="keyword">return</span> key.hashCode() ^ val.hashCode(); }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> String <span class="title">toString</span><span class="params">()</span></span>{ <span class="keyword">return</span> key + <span class="string">"="</span> + val; }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">setValue</span><span class="params">(V value)</span> </span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//同为Map.Entry,key、value不为空且key、value相等,则返回true</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object o)</span> </span>{</span><br><span class="line"> Object k, v, u; Map.Entry<?,?> e;</span><br><span class="line"> <span class="keyword">return</span> ((o <span class="keyword">instanceof</span> Map.Entry) &&</span><br><span class="line"> (k = (e = (Map.Entry<?,?>)o).getKey()) != <span class="keyword">null</span> &&</span><br><span class="line"> (v = e.getValue()) != <span class="keyword">null</span> &&</span><br><span class="line"> (k == key || k.equals(key)) &&</span><br><span class="line"> (v == (u = val) || v.equals(u)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Virtualized support for map.get(); overridden in subclasses.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function">Node<K,V> <span class="title">find</span><span class="params">(<span class="keyword">int</span> h, Object k)</span> </span>{</span><br><span class="line"> Node<K,V> e = <span class="keyword">this</span>;</span><br><span class="line"> <span class="keyword">if</span> (k != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> K ek;</span><br><span class="line"> <span class="keyword">if</span> (e.hash == h &&</span><br><span class="line"> ((ek = e.key) == k || (ek != <span class="keyword">null</span> && k.equals(ek))))</span><br><span class="line"> <span class="keyword">return</span> e;</span><br><span class="line"> } <span class="keyword">while</span> ((e = e.next) != <span class="keyword">null</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="ForwardingNode"><a href="#ForwardingNode" class="headerlink" title="ForwardingNode"></a>ForwardingNode</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * A node inserted at head of bins during transfer operations.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//表示在扩容时该结点跳过该结点</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ForwardingNode</span><<span class="title">K</span>,<span class="title">V</span>> <span class="keyword">extends</span> <span class="title">Node</span><<span class="title">K</span>,<span class="title">V</span>> </span>{</span><br><span class="line"> <span class="keyword">final</span> Node<K,V>[] nextTable;</span><br><span class="line"> ForwardingNode(Node<K,V>[] tab) {</span><br><span class="line"> <span class="keyword">super</span>(MOVED, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">this</span>.nextTable = tab;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function">Node<K,V> <span class="title">find</span><span class="params">(<span class="keyword">int</span> h, Object k)</span> </span>{</span><br><span class="line"> <span class="comment">// loop to avoid arbitrarily deep recursion on forwarding nodes</span></span><br><span class="line"> outer: <span class="keyword">for</span> (Node<K,V>[] tab = nextTable;;) {</span><br><span class="line"> Node<K,V> e; <span class="keyword">int</span> n;</span><br><span class="line"> <span class="keyword">if</span> (k == <span class="keyword">null</span> || tab == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span> ||</span><br><span class="line"> (e = tabAt(tab, (n - <span class="number">1</span>) & h)) == <span class="keyword">null</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">for</span> (;;) {</span><br><span class="line"> <span class="keyword">int</span> eh; K ek;</span><br><span class="line"> <span class="keyword">if</span> ((eh = e.hash) == h &&</span><br><span class="line"> ((ek = e.key) == k || (ek != <span class="keyword">null</span> && k.equals(ek))))</span><br><span class="line"> <span class="keyword">return</span> e;</span><br><span class="line"> <span class="keyword">if</span> (eh < <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> ForwardingNode) {</span><br><span class="line"> tab = ((ForwardingNode<K,V>)e).nextTable;</span><br><span class="line"> <span class="keyword">continue</span> outer;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> e.find(h, k);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ((e = e.next) == <span class="keyword">null</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a>CAS</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Unsafe mechanics</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe U;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> SIZECTL;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> TRANSFERINDEX;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> BASECOUNT;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> CELLSBUSY;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> CELLVALUE;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> ABASE;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ASHIFT;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> U = sun.misc.Unsafe.getUnsafe();</span><br><span class="line"> Class<?> k = ConcurrentHashMap.class;</span><br><span class="line"> SIZECTL = U.objectFieldOffset</span><br><span class="line"> (k.getDeclaredField(<span class="string">"sizeCtl"</span>));</span><br><span class="line"> TRANSFERINDEX = U.objectFieldOffset</span><br><span class="line"> (k.getDeclaredField(<span class="string">"transferIndex"</span>));</span><br><span class="line"> BASECOUNT = U.objectFieldOffset</span><br><span class="line"> (k.getDeclaredField(<span class="string">"baseCount"</span>));</span><br><span class="line"> CELLSBUSY = U.objectFieldOffset</span><br><span class="line"> (k.getDeclaredField(<span class="string">"cellsBusy"</span>));</span><br><span class="line"> Class<?> ck = CounterCell.class;</span><br><span class="line"> CELLVALUE = U.objectFieldOffset</span><br><span class="line"> (ck.getDeclaredField(<span class="string">"value"</span>));</span><br><span class="line"> Class<?> ak = Node[].class;</span><br><span class="line"> ABASE = U.arrayBaseOffset(ak);</span><br><span class="line"> <span class="keyword">int</span> scale = U.arrayIndexScale(ak);</span><br><span class="line"> <span class="keyword">if</span> ((scale & (scale - <span class="number">1</span>)) != <span class="number">0</span>)</span><br><span 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>);</span><br><span class="line"> ASHIFT = <span class="number">31</span> - Integer.numberOfLeadingZeros(scale);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> Error(e);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="tabAt"><a href="#tabAt" class="headerlink" title="tabAt"></a>tabAt</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings("unchecked")</span></span><br><span class="line"><span class="comment">//获得i位置的Node结点</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <K,V> <span class="function">Node<K,V> <span class="title">tabAt</span><span class="params">(Node<K,V>[] tab, <span class="keyword">int</span> i)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (Node<K,V>)U.getObjectVolatile(tab, ((<span class="keyword">long</span>)i << ASHIFT) + ABASE);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用CAS操作更改i位置的Node结点</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <K,V> <span class="function"><span class="keyword">boolean</span> <span class="title">casTabAt</span><span class="params">(Node<K,V>[] tab, <span class="keyword">int</span> i,</span></span></span><br><span class="line"><span class="params"><span class="function"> Node<K,V> c, Node<K,V> v)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> U.compareAndSwapObject(tab, ((<span class="keyword">long</span>)i << ASHIFT) + ABASE, c, v);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置i位置Node结点的值</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <K,V> <span class="function"><span class="keyword">void</span> <span class="title">setTabAt</span><span class="params">(Node<K,V>[] tab, <span class="keyword">int</span> i, Node<K,V> v)</span> </span>{</span><br><span class="line"> U.putObjectVolatile(tab, ((<span class="keyword">long</span>)i << ASHIFT) + ABASE, v);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="有参构造"><a href="#有参构造" class="headerlink" title="有参构造"></a>有参构造</h2><h3 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Creates a new, empty map with an initial table size based on</span></span><br><span class="line"><span class="comment"> * the given number of elements ({<span class="doctag">@code</span> initialCapacity}), table</span></span><br><span class="line"><span class="comment"> * density ({<span class="doctag">@code</span> loadFactor}), and number of concurrently</span></span><br><span class="line"><span class="comment"> * updating threads ({<span class="doctag">@code</span> concurrencyLevel}).</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> initialCapacity the initial capacity. The implementation</span></span><br><span class="line"><span class="comment"> * performs internal sizing to accommodate this many elements,</span></span><br><span class="line"><span class="comment"> * given the specified load factor.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> loadFactor the load factor (table density) for</span></span><br><span class="line"><span class="comment"> * establishing the initial table size</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> concurrencyLevel the estimated number of concurrently</span></span><br><span class="line"><span class="comment"> * updating threads. The implementation may use this value as</span></span><br><span class="line"><span class="comment"> * a sizing hint.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> IllegalArgumentException if the initial capacity is</span></span><br><span class="line"><span class="comment"> * negative or the load factor or concurrencyLevel are</span></span><br><span class="line"><span class="comment"> * nonpositive</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ConcurrentHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">float</span> loadFactor, <span class="keyword">int</span> concurrencyLevel)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!(loadFactor > <span class="number">0.0f</span>) || initialCapacity < <span class="number">0</span> || concurrencyLevel <= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line"> <span class="keyword">if</span> (initialCapacity < concurrencyLevel) <span class="comment">// Use at least as many bins</span></span><br><span class="line"> initialCapacity = concurrencyLevel; <span class="comment">// as estimated threads</span></span><br><span class="line"> <span class="keyword">long</span> size = (<span class="keyword">long</span>)(<span class="number">1.0</span> + (<span class="keyword">long</span>)initialCapacity / loadFactor);</span><br><span class="line"> <span class="keyword">int</span> cap = (size >= (<span class="keyword">long</span>)MAXIMUM_CAPACITY) ?</span><br><span class="line"> MAXIMUM_CAPACITY : tableSizeFor((<span class="keyword">int</span>)size);</span><br><span class="line"> <span class="keyword">this</span>.sizeCtl = cap;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="初始化table"><a href="#初始化table" class="headerlink" title="初始化table"></a>初始化table</h2><h3 id="initTable"><a href="#initTable" class="headerlink" title="initTable"></a>initTable</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Initializes table, using the size recorded in sizeCtl.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Node<K,V>[] initTable() {</span><br><span class="line"> Node<K,V>[] tab; <span class="keyword">int</span> sc;</span><br><span class="line"> <span class="comment">//当table未初始化或长度为0</span></span><br><span class="line"> <span class="keyword">while</span> ((tab = table) == <span class="keyword">null</span> || tab.length == <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">//表明有其他线程正在进行初始化table,当前线程礼让其他线程</span></span><br><span class="line"> <span class="keyword">if</span> ((sc = sizeCtl) < <span class="number">0</span>)</span><br><span class="line"> Thread.yield(); <span class="comment">// lost initialization race; just spin</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//使用CAS操作把sizeCtl置为-1,表示本线程进行初始化table</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc, -<span class="number">1</span>)) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">//table未初始化或长度为0</span></span><br><span class="line"> <span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || tab.length == <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">//private static final int DEFAULT_CAPACITY = 16;</span></span><br><span class="line"> <span class="keyword">int</span> n = (sc > <span class="number">0</span>) ? sc : DEFAULT_CAPACITY;</span><br><span class="line"> <span class="meta">@SuppressWarnings("unchecked")</span></span><br><span class="line"> Node<K,V>[] nt = (Node<K,V>[])<span class="keyword">new</span> Node<?,?>[n];</span><br><span class="line"> table = tab = nt;</span><br><span class="line"> <span class="comment">//扩容阈值为0.75n</span></span><br><span class="line"> sc = n - (n >>> <span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> sizeCtl = sc;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> tab;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="put方法"><a href="#put方法" class="headerlink" title="put方法"></a>put方法</h2><h3 id="put"><a href="#put" class="headerlink" title="put"></a>put</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Maps the specified key to the specified value in this table.</span></span><br><span class="line"><span class="comment"> * Neither the key nor the value can be null.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <p>The value can be retrieved by calling the {<span class="doctag">@code</span> get} method</span></span><br><span class="line"><span class="comment"> * with a key that is equal to the original key.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> key key with which the specified value is to be associated</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value value to be associated with the specified key</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> the previous value associated with {<span class="doctag">@code</span> key}, or</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@code</span> null} if there was no mapping for {<span class="doctag">@code</span> key}</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NullPointerException if the specified key or value is null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//key、value非空</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> putVal(key, value, <span class="keyword">false</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="putVal"><a href="#putVal" class="headerlink" title="putVal"></a>putVal</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Implementation for put and putIfAbsent */</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(K key, V value, <span class="keyword">boolean</span> onlyIfAbsent)</span> </span>{</span><br><span class="line"> <span class="comment">//key、value非空</span></span><br><span class="line"> <span class="keyword">if</span> (key == <span class="keyword">null</span> || value == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line"> <span class="comment">//计算hash值</span></span><br><span class="line"> <span class="keyword">int</span> hash = spread(key.hashCode());</span><br><span class="line"> <span class="keyword">int</span> binCount = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">//死循环 插入成功后退出循环</span></span><br><span class="line"> <span class="keyword">for</span> (Node<K,V>[] tab = table;;) {</span><br><span class="line"> Node<K,V> f; <span class="keyword">int</span> n, i, fh;</span><br><span class="line"> <span class="comment">//table未初始化或长度为0,进行初始化</span></span><br><span class="line"> <span class="keyword">if</span> (tab == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line"> tab = initTable();</span><br><span class="line"> <span class="comment">//该结点上无元素,直接插入,没有加锁</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i = (n - <span class="number">1</span>) & hash)) == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">if</span> (casTabAt(tab, i, <span class="keyword">null</span>,</span><br><span class="line"> <span class="keyword">new</span> Node<K,V>(hash, key, value, <span class="keyword">null</span>)))</span><br><span class="line"> <span class="keyword">break</span>; <span class="comment">// no lock when adding to empty bin</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//表明table正在进行扩容</span></span><br><span class="line"> <span class="comment">//static final int MOVED = -1; // hash for forwarding nodes</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</span><br><span class="line"> <span class="comment">//协助table扩容</span></span><br><span class="line"> tab = helpTransfer(tab, f);</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> V oldVal = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//该结点元素不为空,加锁</span></span><br><span class="line"> <span class="keyword">synchronized</span> (f) {</span><br><span class="line"> <span class="comment">//该结点元素未被其他线程覆盖</span></span><br><span class="line"> <span class="keyword">if</span> (tabAt(tab, i) == f) {</span><br><span class="line"> <span class="comment">//说明这是一条链表</span></span><br><span class="line"> <span class="keyword">if</span> (fh >= <span class="number">0</span>) {</span><br><span class="line"> binCount = <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//遍历链表</span></span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> e = f;; ++binCount) {</span><br><span class="line"> K ek;</span><br><span class="line"> <span class="comment">//如果key相同,则需要修改value值,退出循环</span></span><br><span class="line"> <span class="keyword">if</span> (e.hash == hash &&</span><br><span class="line"> ((ek = e.key) == key ||</span><br><span class="line"> (ek != <span class="keyword">null</span> && key.equals(ek)))) {</span><br><span class="line"> oldVal = e.val;</span><br><span class="line"> <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line"> e.val = value;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> Node<K,V> pred = e;</span><br><span class="line"> <span class="comment">//遍历完链表未找到key相等的结点,则在链表尾部插入一个结点,并退出循环</span></span><br><span class="line"> <span class="keyword">if</span> ((e = e.next) == <span class="keyword">null</span>) {</span><br><span class="line"> pred.next = <span class="keyword">new</span> Node<K,V>(hash, key,</span><br><span class="line"> value, <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//static final int TREEBIN = -2; // hash for roots of trees</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) {</span><br><span class="line"> Node<K,V> p;</span><br><span class="line"> binCount = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span> ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,</span><br><span class="line"> value)) != <span class="keyword">null</span>) {</span><br><span class="line"> oldVal = p.val;</span><br><span class="line"> <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line"> p.val = value;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (binCount != <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">//如果链表长度达到临界值,则转变为红黑树</span></span><br><span class="line"> <span class="comment">//static final int TREEIFY_THRESHOLD = 8;</span></span><br><span class="line"> <span class="keyword">if</span> (binCount >= TREEIFY_THRESHOLD)</span><br><span class="line"> treeifyBin(tab, i);</span><br><span class="line"> <span class="keyword">if</span> (oldVal != <span class="keyword">null</span>)</span><br><span class="line"> <span class="keyword">return</span> oldVal;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将ConcurrentHashMap中元素数量+1</span></span><br><span class="line"> addCount(<span class="number">1L</span>, binCount);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="spread"><a href="#spread" class="headerlink" title="spread"></a>spread</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Spreads (XORs) higher bits of hash to lower and also forces top</span></span><br><span class="line"><span class="comment"> * bit to 0. Because the table uses power-of-two masking, sets of</span></span><br><span class="line"><span class="comment"> * hashes that vary only in bits above the current mask will</span></span><br><span class="line"><span class="comment"> * always collide. (Among known examples are sets of Float keys</span></span><br><span class="line"><span class="comment"> * holding consecutive whole numbers in small tables.) So we</span></span><br><span class="line"><span class="comment"> * apply a transform that spreads the impact of higher bits</span></span><br><span class="line"><span class="comment"> * downward. There is a tradeoff between speed, utility, and</span></span><br><span class="line"><span class="comment"> * quality of bit-spreading. Because many common sets of hashes</span></span><br><span class="line"><span class="comment"> * are already reasonably distributed (so don't benefit from</span></span><br><span class="line"><span class="comment"> * spreading), and because we use trees to handle large sets of</span></span><br><span class="line"><span class="comment"> * collisions in bins, we just XOR some shifted bits in the</span></span><br><span class="line"><span class="comment"> * cheapest possible way to reduce systematic lossage, as well as</span></span><br><span class="line"><span class="comment"> * to incorporate impact of the highest bits that would otherwise</span></span><br><span class="line"><span class="comment"> * never be used in index calculations because of table bounds.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//与hashmap的hash方法相似,高16位与低16位异或并保证最高位为0(从而保证最终结果为正整数)</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">spread</span><span class="params">(<span class="keyword">int</span> h)</span> </span>{</span><br><span class="line"> <span class="comment">//static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash</span></span><br><span class="line"> <span class="keyword">return</span> (h ^ (h >>> <span class="number">16</span>)) & HASH_BITS;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="扩容方法"><a href="#扩容方法" class="headerlink" title="扩容方法"></a>扩容方法</h2><h3 id="addCount"><a href="#addCount" class="headerlink" title="addCount"></a>addCount</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Adds to count, and if table is too small and not already</span></span><br><span class="line"><span class="comment"> * resizing, initiates transfer. If already resizing, helps</span></span><br><span class="line"><span class="comment"> * perform transfer if work is available. Rechecks occupancy</span></span><br><span class="line"><span class="comment"> * after a transfer to see if another resize is already needed</span></span><br><span class="line"><span class="comment"> * because resizings are lagging additions.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> x the count to add</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> check if <0, don't check resize, if <= 1 only check if uncontended</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">addCount</span><span class="params">(<span class="keyword">long</span> x, <span class="keyword">int</span> check)</span> </span>{</span><br><span class="line"> CounterCell[] as; <span class="keyword">long</span> b, s;</span><br><span class="line"> <span class="comment">//使用CAS更新baseCount的值</span></span><br><span class="line"> <span class="keyword">if</span> ((as = counterCells) != <span class="keyword">null</span> ||</span><br><span class="line"> !U.compareAndSwapLong(<span class="keyword">this</span>, BASECOUNT, b = baseCount, s = b + x)) {</span><br><span class="line"> CounterCell a; <span class="keyword">long</span> v; <span class="keyword">int</span> m;</span><br><span class="line"> <span class="keyword">boolean</span> uncontended = <span class="keyword">true</span>;</span><br><span 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> ||</span><br><span class="line"> (a = as[ThreadLocalRandom.getProbe() & m]) == <span class="keyword">null</span> ||</span><br><span class="line"> !(uncontended =</span><br><span class="line"> U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {</span><br><span class="line"> fullAddCount(x, uncontended);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (check <= <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> s = sumCount();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果check大于等于0,检查是否需要扩容</span></span><br><span class="line"> <span class="keyword">if</span> (check >= <span class="number">0</span>) {</span><br><span class="line"> Node<K,V>[] tab, nt; <span class="keyword">int</span> n, sc;</span><br><span class="line"> <span class="keyword">while</span> (s >= (<span class="keyword">long</span>)(sc = sizeCtl) && (tab = table) != <span class="keyword">null</span> &&</span><br><span class="line"> (n = tab.length) < MAXIMUM_CAPACITY) {</span><br><span class="line"> <span class="keyword">int</span> rs = resizeStamp(n);</span><br><span class="line"> <span class="comment">//表示table正在初始化或扩容</span></span><br><span class="line"> <span class="keyword">if</span> (sc < <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + <span class="number">1</span> ||</span><br><span class="line"> sc == rs + MAX_RESIZERS || (nt = nextTable) == <span class="keyword">null</span> ||</span><br><span class="line"> transferIndex <= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="comment">//如果已有其他线程正在进行扩容,协助其他线程扩容</span></span><br><span class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc, sc + <span class="number">1</span>))</span><br><span class="line"> transfer(tab, nt);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//当前线程是第一个发起扩容或唯一一个正在扩容的线程</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc,</span><br><span class="line"> (rs << RESIZE_STAMP_SHIFT) + <span class="number">2</span>))</span><br><span class="line"> transfer(tab, <span class="keyword">null</span>);</span><br><span class="line"> s = sumCount();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="helpTransfer"><a href="#helpTransfer" class="headerlink" title="helpTransfer"></a>helpTransfer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Helps transfer if a resize is in progress.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//协助扩容</span></span><br><span class="line"><span class="keyword">final</span> Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {</span><br><span class="line"> Node<K,V>[] nextTab; <span class="keyword">int</span> sc;</span><br><span class="line"> <span class="comment">//table不为空,当前结点为ForwardingNode结点,且ForwardingNode的nextTable属性不为空</span></span><br><span class="line"> <span class="keyword">if</span> (tab != <span class="keyword">null</span> && (f <span class="keyword">instanceof</span> ForwardingNode) &&</span><br><span class="line"> (nextTab = ((ForwardingNode<K,V>)f).nextTable) != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="comment">//计算检验码</span></span><br><span class="line"> <span class="keyword">int</span> rs = resizeStamp(tab.length);</span><br><span class="line"> <span class="keyword">while</span> (nextTab == nextTable && table == tab &&</span><br><span class="line"> (sc = sizeCtl) < <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + <span class="number">1</span> ||</span><br><span class="line"> sc == rs + MAX_RESIZERS || transferIndex <= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="comment">//如果已有其他线程正在进行扩容,协助其他线程扩容</span></span><br><span class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc, sc + <span class="number">1</span>)) {</span><br><span class="line"> transfer(tab, nextTab);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> nextTab;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> table;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="transfer"><a href="#transfer" class="headerlink" title="transfer"></a>transfer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Moves and/or copies the nodes in each bin to new table. See</span></span><br><span class="line"><span class="comment"> * above for explanation.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Node<K,V>[] tab, Node<K,V>[] nextTab)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> n = tab.length, stride;</span><br><span class="line"> <span class="comment">//将length/8然后除以CPU核心数。如果得到的结果小于16,那么就使用16</span></span><br><span class="line"> <span class="comment">//这里的目的是让每个CPU处理的桶一样多,避免出现转移任务不均匀的现象,如果桶较少的话,默认一个CPU(一个线程)</span></span><br><span class="line"> <span class="keyword">if</span> ((stride = (NCPU > <span class="number">1</span>) ? (n >>> <span class="number">3</span>) / NCPU : n) < MIN_TRANSFER_STRIDE)</span><br><span class="line"> stride = MIN_TRANSFER_STRIDE; <span class="comment">// subdivide range</span></span><br><span class="line"> <span class="comment">//如果新table为空</span></span><br><span class="line"> <span class="keyword">if</span> (nextTab == <span class="keyword">null</span>) { <span class="comment">// initiating</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="meta">@SuppressWarnings("unchecked")</span></span><br><span class="line"> <span class="comment">//创建容量为原table2倍的新table</span></span><br><span class="line"> Node<K,V>[] nt = (Node<K,V>[])<span class="keyword">new</span> Node<?,?>[n << <span class="number">1</span>];</span><br><span class="line"> nextTab = nt;</span><br><span class="line"> } <span class="keyword">catch</span> (Throwable ex) { <span class="comment">// try to cope with OOME</span></span><br><span class="line"> sizeCtl = Integer.MAX_VALUE;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> nextTable = nextTab;</span><br><span class="line"> transferIndex = n;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> nextn = nextTab.length;</span><br><span class="line"> <span class="comment">//用于占位,线程发现某一结点为ForwardingNode,则跳过该结点</span></span><br><span class="line"> ForwardingNode<K,V> fwd = <span class="keyword">new</span> ForwardingNode<K,V>(nextTab);</span><br><span class="line"> <span class="comment">//如果advance等于true 表明该结点已处理过</span></span><br><span class="line"> <span class="keyword">boolean</span> advance = <span class="keyword">true</span>;</span><br><span class="line"> <span class="keyword">boolean</span> finishing = <span class="keyword">false</span>; <span class="comment">// to ensure sweep before committing nextTab</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, bound = <span class="number">0</span>;;) {</span><br><span class="line"> Node<K,V> f; <span class="keyword">int</span> fh;</span><br><span class="line"> <span class="comment">//遍历所有结点</span></span><br><span class="line"> <span class="keyword">while</span> (advance) {</span><br><span class="line"> <span class="keyword">int</span> nextIndex, nextBound;</span><br><span class="line"> <span class="keyword">if</span> (--i >= bound || finishing)</span><br><span class="line"> advance = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((nextIndex = transferIndex) <= <span class="number">0</span>) {</span><br><span class="line"> i = -<span class="number">1</span>;</span><br><span class="line"> advance = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt</span><br><span class="line"> (<span class="keyword">this</span>, TRANSFERINDEX, nextIndex,</span><br><span class="line"> nextBound = (nextIndex > stride ?</span><br><span class="line"> nextIndex - stride : <span class="number">0</span>))) {</span><br><span class="line"> bound = nextBound;</span><br><span class="line"> i = nextIndex - <span class="number">1</span>;</span><br><span class="line"> advance = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (i < <span class="number">0</span> || i >= n || i + n >= nextn) {</span><br><span class="line"> <span class="keyword">int</span> sc;</span><br><span class="line"> <span class="comment">//结点完成复制工作</span></span><br><span class="line"> <span class="keyword">if</span> (finishing) {</span><br><span class="line"> <span class="comment">//清空临时变量nextTable</span></span><br><span class="line"> nextTable = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">//将table赋值为nextTab</span></span><br><span class="line"> table = nextTab;</span><br><span class="line"> <span class="comment">//阈值还是当前容量的0.75</span></span><br><span class="line"> sizeCtl = (n << <span class="number">1</span>) - (n >>> <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//使用CAS更新扩容阈值</span></span><br><span class="line"> <span class="comment">//sc-1表明有新的线程加入扩容</span></span><br><span class="line"> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="keyword">this</span>, SIZECTL, sc = sizeCtl, sc - <span class="number">1</span>)) {</span><br><span class="line"> <span class="keyword">if</span> ((sc - <span class="number">2</span>) != resizeStamp(n) << RESIZE_STAMP_SHIFT)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> finishing = advance = <span class="keyword">true</span>;</span><br><span class="line"> i = n; <span class="comment">// recheck before commit</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果结点为null,放入ForwardingNode</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i)) == <span class="keyword">null</span>)</span><br><span class="line"> advance = casTabAt(tab, i, <span class="keyword">null</span>, fwd);</span><br><span class="line"> <span class="comment">//当前结点已经被处理</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</span><br><span class="line"> advance = <span class="keyword">true</span>; <span class="comment">// already processed</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//结点加锁</span></span><br><span class="line"> <span class="keyword">synchronized</span> (f) {</span><br><span class="line"> <span class="comment">//该结点未被其他线程覆盖</span></span><br><span class="line"> <span class="keyword">if</span> (tabAt(tab, i) == f) {</span><br><span class="line"> Node<K,V> ln, hn;</span><br><span class="line"> <span class="comment">//链表</span></span><br><span class="line"> <span class="keyword">if</span> (fh >= <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">int</span> runBit = fh & n;</span><br><span class="line"> <span class="comment">//构造两个链表一个为原链表,一个为反向链表</span></span><br><span class="line"> Node<K,V> lastRun = f;</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> p = f.next; p != <span class="keyword">null</span>; p = p.next) {</span><br><span class="line"> <span class="keyword">int</span> b = p.hash & n;</span><br><span class="line"> <span class="keyword">if</span> (b != runBit) {</span><br><span class="line"> runBit = b;</span><br><span class="line"> lastRun = p;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (runBit == <span class="number">0</span>) {</span><br><span class="line"> ln = lastRun;</span><br><span class="line"> hn = <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> hn = lastRun;</span><br><span class="line"> ln = <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> p = f; p != lastRun; p = p.next) {</span><br><span class="line"> <span class="keyword">int</span> ph = p.hash; K pk = p.key; V pv = p.val;</span><br><span class="line"> <span class="keyword">if</span> ((ph & n) == <span class="number">0</span>)</span><br><span class="line"> ln = <span class="keyword">new</span> Node<K,V>(ph, pk, pv, ln);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> hn = <span class="keyword">new</span> Node<K,V>(ph, pk, pv, hn);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//插入链表</span></span><br><span class="line"> setTabAt(nextTab, i, ln);</span><br><span class="line"> setTabAt(nextTab, i + n, hn);</span><br><span class="line"> <span class="comment">//放入ForwardingNode,表明该结点已被处理</span></span><br><span class="line"> setTabAt(tab, i, fwd);</span><br><span class="line"> advance = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//与链表操作相似</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) {</span><br><span class="line"> TreeBin<K,V> t = (TreeBin<K,V>)f;</span><br><span class="line"> TreeNode<K,V> lo = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</span><br><span class="line"> TreeNode<K,V> hi = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">int</span> lc = <span class="number">0</span>, hc = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (Node<K,V> e = t.first; e != <span class="keyword">null</span>; e = e.next) {</span><br><span class="line"> <span class="keyword">int</span> h = e.hash;</span><br><span class="line"> TreeNode<K,V> p = <span class="keyword">new</span> TreeNode<K,V></span><br><span class="line"> (h, e.key, e.val, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">if</span> ((h & n) == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> ((p.prev = loTail) == <span class="keyword">null</span>)</span><br><span class="line"> lo = p;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> loTail.next = p;</span><br><span class="line"> loTail = p;</span><br><span class="line"> ++lc;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> ((p.prev = hiTail) == <span class="keyword">null</span>)</span><br><span class="line"> hi = p;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> hiTail.next = p;</span><br><span class="line"> hiTail = p;</span><br><span class="line"> ++hc;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :</span><br><span class="line"> (hc != <span class="number">0</span>) ? <span class="keyword">new</span> TreeBin<K,V>(lo) : t;</span><br><span class="line"> hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :</span><br><span class="line"> (lc != <span class="number">0</span>) ? <span class="keyword">new</span> TreeBin<K,V>(hi) : t;</span><br><span class="line"> setTabAt(nextTab, i, ln);</span><br><span class="line"> setTabAt(nextTab, i + n, hn);</span><br><span class="line"> setTabAt(tab, i, fwd);</span><br><span class="line"> advance = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java基础 </category>
</categories>