-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.xml
More file actions
2292 lines (2272 loc) · 375 KB
/
index.xml
File metadata and controls
2292 lines (2272 loc) · 375 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>pipo's site</title>
<link>https://asgpipo.github.io/</link>
<description>Recent content on pipo's site</description>
<generator>Hugo -- gohugo.io</generator>
<language>en</language><atom:link href="https://asgpipo.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Spring 中的 Bind</title>
<link>https://asgpipo.github.io/posts/spring/bind/</link>
<pubDate>Thu, 22 Jan 2026 14:44:35 +0800</pubDate>
<guid>https://asgpipo.github.io/posts/spring/bind/</guid>
<description><h2 id="bind-是什么">Bind 是什么?</h2>
<p>Spring Boot 的 Bind 机制主要负责将 <strong>外部配置</strong>(如 <code>application.yml</code>、<code>application.properties</code>、环境变量等)绑定到 <strong>Java 对象</strong> 中。</p>
<p>外部配置绑定优先级:</p>
<ol>
<li>命令行参数(<code>java -jar app.jar --db.port=9000</code>)</li>
<li>操作系统环境变量(<code>export DB_PORT=9000</code>)</li>
<li>jar 包外部的配置文件(<code>/config/application.yaml</code>)</li>
<li>jar 包内部的配置文件(<code>resources/application.yml</code>)</li>
</ol>
<h2 id="三种绑定方式">三种绑定方式</h2>
<h3 id="value">@Value</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Component</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">MyService</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Value</span><span class="p">(</span><span class="s">&#34;${app.name:default-app}&#34;</span><span class="p">)</span><span class="w"> </span><span class="c1">// 支持默认值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">appName</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Value</span><span class="p">(</span><span class="s">&#34;${app.feature.enabled:false}&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">featureEnabled</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Value</span><span class="p">(</span><span class="s">&#34;#{systemProperties[&#39;user.home&#39;]}&#34;</span><span class="p">)</span><span class="w"> </span><span class="c1">// SpEL 表达式</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">userHome</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p><strong>限制:</strong></p>
<ul>
<li>不支持 JSR-303 校验</li>
<li>不支持复杂对象绑定</li>
<li>不支持松散绑定</li>
<li>无法在配置文件中提示 IDE 支持</li>
</ul>
<hr>
<h3 id="configurationproperties">@ConfigurationProperties</h3>
<p><code>@ConfigurationProperties( prefix = &quot;&quot;)</code>
外部配置文件:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">myapp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;myapp&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">server</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="m">1.1.1.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">security</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">admin</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="m">123</span><span class="w">
</span></span></span></code></pre></div><p>配置方式</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Component</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@ConfigurationProperties</span><span class="p">(</span><span class="n">prefix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;myapp&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">MyAppProperties</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 必须要提供 getter setter,可以使用 Lombook 简化</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">ServerConfig</span><span class="w"> </span><span class="n">server</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">ServerSecurity</span><span class="w"> </span><span class="n">security</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//嵌套配置 创建类, 必须是 public static</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">class</span> <span class="nc">ServerConfig</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">host</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="n">port</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">class</span> <span class="nc">ServerSecurity</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">username</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">password</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>使用方式</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">AppConfig</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Autowired</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">MyAppProperties</span><span class="w"> </span><span class="n">properties</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>
<p>prefix 的使用:
prefix 填入 要被装载的属性的前缀,如果有多个嵌套则可以 <code>myapp.server.XXX.XXX</code> 来装载对应层级下的属性</p>
</li>
<li>
<p>松散绑定:
Spring 可以自动将 配置属性的名词与定义的类中的名称进行自动映射
支持的类型名称:</p>
<ul>
<li>kebab-case (烤肉串式): 全小写,单词间用连字符分隔,如 my-variable-name</li>
<li>snake_case (蛇形): 全小写,单词间用下划线分隔,如 my_variable_name</li>
<li>UPPERCASE (全大写): 全部大写,通常用于常量,如 MY_CONSTANT</li>
</ul>
</li>
<li>
<p>JSR-303 校验:
在类上添加 @Validated 开启校验</p>
<ul>
<li>@NotNull</li>
<li>@Min()</li>
<li>@Max()</li>
<li>@NotBlank</li>
<li>@Email</li>
<li>@Size(min = )</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Component</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@ConfigurationProperties</span><span class="p">(</span><span class="n">prefix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;myapp&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Validated</span><span class="w"> </span><span class="c1">// &lt;--- 开启第一层校验</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">MyAppProperties</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Valid</span><span class="w"> </span><span class="c1">// &lt;--- 关键!告诉 Spring 钻进去校验内部属性</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">ServerConfig</span><span class="w"> </span><span class="n">server</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// ...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div></li>
</ul>
<hr>
<h3 id="binder-api">Binder API</h3>
<p>使用 Binder API 读取 Environment 中的配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Bind</span><span class="w"> </span><span class="n">bind</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Binder</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">environment</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h4 id="读取单个值">读取单个值</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 读取单个值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="n">host</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bind</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="s">&#34;myapp.server.host&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="p">.</span><span class="na">class</span><span class="p">).</span><span class="na">orElse</span><span class="p">(</span><span class="s">&#34;1.1.1.1&#34;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>读取单个值要写入完整的路径,并且使用点号分割</p>
</blockquote>
<hr>
<h4 id="读取复杂类型-listmap"><strong>读取复杂类型 (List/Map)</strong></h4>
<p>如果在 application.yaml 里写了一个 List:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">my-app</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">whitelist</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="m">192.168.1.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="m">10.0.0.1</span><span class="w">
</span></span></span></code></pre></div><p>需要使用 Bindable 包装器处理范型</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ips</span><span class="w"> </span><span class="o">=</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">binder</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="w"> </span><span class="s">&#34;my-app.whitelist&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Bindable</span><span class="p">.</span><span class="na">listOf</span><span class="p">(</span><span class="n">String</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span><span class="c1">// &lt;--- 关键!告诉 Binder 这是一个 List&lt;String&gt; ).orElse(Collections.emptyList());</span><span class="w">
</span></span></span></code></pre></div><p>如果是更复杂的 比如</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">routes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;order-service&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/api/order&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://localhost:8081&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;user-service&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/api/user&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://localhost:8082&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">private List&lt;Map&lt;Object, Object&gt;&gt; routes = new ArrayList&lt;&gt;();</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">1</span><span class="p">.</span><span class="na">方法一</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">&#34;unchecked&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">List</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">Object</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">routes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">Object</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="o">&lt;?&gt;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">binder</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="s">&#34;gateway.routes&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Bindable</span><span class="p">.</span><span class="na">listOf</span><span class="p">(</span><span class="n">Map</span><span class="p">.</span><span class="na">class</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElse</span><span class="p">(</span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">2</span><span class="p">.</span><span class="na">方法二</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">List</span><span class="o">&lt;?&gt;</span><span class="w"> </span><span class="n">routes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">binder</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="s">&#34;gateway.routes&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Bindable</span><span class="p">.</span><span class="na">listOf</span><span class="p">(</span><span class="n">Map</span><span class="p">.</span><span class="na">class</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElse</span><span class="p">(</span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">3</span><span class="p">.</span><span class="na">方法三</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">RouteConfig</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">id</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">path</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">List</span><span class="o">&lt;</span><span class="n">RouteConfig</span><span class="o">&gt;</span><span class="w"> </span><span class="n">routes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">binder</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="s">&#34;gateway.routes&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Bindable</span><span class="p">.</span><span class="na">listOf</span><span class="p">(</span><span class="n">RouteConfig</span><span class="p">.</span><span class="na">class</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElse</span><span class="p">(</span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">());</span><span class="w">
</span></span></span></code></pre></div><hr>
<h4 id="读取整个配置类">读取整个配置类</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">//后面的 XXX.class 是在告诉 “我要读取的是什么类型的配置请转换成这种类型 &#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">MyAppProperties</span><span class="w"> </span><span class="n">properties</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bind</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="s">&#34;myapp&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">MyAppProperties</span><span class="p">.</span><span class="na">class</span><span class="p">).</span><span class="na">get</span><span class="p">();</span><span class="w">
</span></span></span></code></pre></div><hr>
<h4 id="创建本地-bind-并绑定">创建本地 bind 并绑定</h4>
<p>创建本地 bind 并添加配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Binder</span><span class="w"> </span><span class="n">binder</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Binder</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">environment</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//添加 配置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">1</span><span class="p">.</span><span class="w"> </span><span class="n">方法一</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="w"> </span><span class="n">source1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">map1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="w"> </span><span class="n">source2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">map2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Binder</span><span class="w"> </span><span class="n">localBind</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Binder</span><span class="p">(</span><span class="n">source1</span><span class="p">,</span><span class="w"> </span><span class="n">source2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">2</span><span class="p">.</span><span class="w"> </span><span class="n">方法二</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">ConfigurationPropertySource</span><span class="o">&gt;</span><span class="w"> </span><span class="n">sources</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">sources</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">map1</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">sources</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">map2</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">sources</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">map3</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Binder</span><span class="w"> </span><span class="n">localBind</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Binder</span><span class="p">(</span><span class="n">sources</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span></code></pre></div><p>生产中建议</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="n">Binder</span><span class="w"> </span><span class="nf">createLocalBinder</span><span class="p">(</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;</span><span class="p">...</span><span class="w"> </span><span class="n">configMaps</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">ConfigurationPropertySource</span><span class="o">&gt;</span><span class="w"> </span><span class="n">sources</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;</span><span class="w"> </span><span class="n">map</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">configMaps</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="o">!</span><span class="n">map</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">sources</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">map</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Binder</span><span class="p">(</span><span class="n">sources</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// 使用</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Binder</span><span class="w"> </span><span class="n">localBind</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">createLocalBinder</span><span class="p">(</span><span class="n">legacyMap</span><span class="p">,</span><span class="w"> </span><span class="n">otherMap</span><span class="p">,</span><span class="w"> </span><span class="n">thirdMap</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p><strong>将 bind 中的值绑定类</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">//RouteDefinition.java</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">RouteDefinition</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">id</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">path</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;</span><span class="w"> </span><span class="n">legacyMap</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HashMap</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">legacyMap</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;legacy-service-999&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">legacyMap</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;path&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;/api/old&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">legacyMap</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;http://192.168.1.100&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//Bind 无法直接绑定 Map 需要转换</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">MapConfigurationPropertySource</span><span class="w"> </span><span class="n">legacyConfigurationMap</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">MapConfigurationPropertySource</span><span class="p">(</span><span class="n">legacyMap</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Binder</span><span class="w"> </span><span class="n">localBind</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Binder</span><span class="p">(</span><span class="n">legacyConfigurationMap</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//为什么这里是 &#34;&#34; 空字符串?</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">RouteDefinition</span><span class="w"> </span><span class="n">legacyRoute</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">localBind</span><span class="p">.</span><span class="na">bind</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">RouteDefinition</span><span class="p">.</span><span class="na">class</span><span class="p">).</span><span class="na">orElse</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">RouteDefinition</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">legacyRoute</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>prefix + 要注入的字段名 = key
此时 key 与 字段名完全相同,所以使用空字符串
如果 legacyMap 中 put 时 key 是 legacy.route.id
那么在将 source 中的属性绑定到 RouteDefinition 时 prefix = &ldquo;legacy.route&rdquo;</p>
</blockquote>
<hr>
<h2 id="三种绑定方式对比">三种绑定方式对比</h2>
<table>
<thead>
<tr>
<th>特性</th>
<th>@Value</th>
<th>@ConfigurationProperties</th>
<th>Binder API</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>使用场景</strong></td>
<td>1-2 个简单配置</td>
<td>结构化配置、多个相关配置</td>
<td>动态绑定、运行时绑定</td>
</tr>
<tr>
<td><strong>JSR-303 校验</strong></td>
<td>❌ 不支持</td>
<td>✅ 支持(@Validated)</td>
<td>✅ 支持</td>
</tr>
<tr>
<td><strong>松散绑定</strong></td>
<td>❌ 不支持</td>
<td>✅ 支持</td>
<td>✅ 支持</td>
</tr>
<tr>
<td><strong>复杂对象</strong></td>
<td>❌ 不支持</td>
<td>✅ 支持(嵌套对象)</td>
<td>✅ 支持</td>
</tr>
<tr>
<td><strong>IDE 元数据支持</strong></td>
<td>❌ 无提示</td>
<td>✅ 有提示(spring-configuration-metadata.json)</td>
<td>❌ 无提示</td>
</tr>
<tr>
<td><strong>类型安全</strong></td>
<td>⚠️ 编译时不检查</td>
<td>✅ 强类型检查</td>
<td>✅ 强类型检查</td>
</tr>
<tr>
<td><strong>集合类型</strong></td>
<td>⚠️ 需要手动解析</td>
<td>✅ 自动绑定 List/Set/Map</td>
<td>✅ 自动绑定(需要 Bindable)</td>
</tr>
<tr>
<td><strong>默认值</strong></td>
<td>✅ <code>${prop:default}</code></td>
<td>✅ 字段初始化或 @DefaultValue</td>
<td>✅ <code>.withDefaultValue()</code></td>
</tr>
<tr>
<td><strong>灵活性</strong></td>
<td>低</td>
<td>中</td>
<td>高</td>
</tr>
</tbody>
</table>
<ol>
<li><strong>优先使用 @ConfigurationProperties</strong> - 类型安全、IDE 支持、可校验</li>
<li><strong>简单配置用 @Value</strong> - 如端口、开关等单个属性</li>
<li><strong>动态配置用 Binder API</strong> - 如从数据库、Nacos 读取配置后绑定</li>
</ol>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config">Spring Boot Configuration Properties</a></li>
<li><a href="https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/ConfigurationProperties.html">Spring Boot @ConfigurationProperties</a></li>
<li><a href="https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/bind/Bindable.html">Spring Boot Bindable API</a></li>
<li><a href="https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/bind/Binder.html">Spring Boot Binder API</a></li>
<li><a href="https://docs.spring.io/spring-boot/docs/current/reference/html/configuration-metadata.html">Configuration Metadata - Spring Boot</a></li>
<li><a href="https://www.baeldung.com/configuration-properties-in-spring-boot">Guide to @ConfigurationProperties in Spring Boot | Baeldung</a></li>
</ul>
</description>
</item>
<item>
<title>从 Future 到 CompletableFuture</title>
<link>https://asgpipo.github.io/posts/java/concurrency/%E4%BB%8E-future-%E5%88%B0-completablefuture/</link>
<pubDate>Mon, 29 Dec 2025 15:37:35 +0800</pubDate>
<guid>https://asgpipo.github.io/posts/java/concurrency/%E4%BB%8E-future-%E5%88%B0-completablefuture/</guid>
<description><h2 id="使用-future-完成多线程任务-threadpoolexecutor">使用 Future 完成多线程任务 (ThreadPoolExecutor)</h2>
<h3 id="快速上手">快速上手</h3>
<ol>
<li>
<p>创建 线程池</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">ThreadPoolExecutor</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">10</span><span class="p">,</span><span class="w"> </span><span class="n">20</span><span class="p">,</span><span class="w"> </span><span class="n">30</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">,</span><span class="w"> </span><span class="n">queue</span><span class="p">,</span><span class="w"> </span><span class="n">Executors</span><span class="p">.</span><span class="na">defaultThreadFactory</span><span class="p">());</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p>完成多线程任务要执行的逻辑</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 创建一个新的类 实现 Callable 接口中的 call() 方法</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Task</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Callable</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="nf">call</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;the Thread name is &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">getName</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p>创建 Future 实例提交任务</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Future</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="n">future</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">submit</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Task</span><span class="p">());</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>⚠️ 注意
submit() 只是提交任务到线程池,并非立即执行任务,执行时机由线程池自行决定。</p>
</blockquote>
</li>
<li>
<p>获取返回值</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Long</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">future</span><span class="p">.</span><span class="na">get</span><span class="p">();</span><span class="w"> </span><span class="c1">// 阻塞式无限时返回</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">Long</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">future</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">1</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">);</span><span class="w"> </span><span class="c1">// 阻塞式限时返回</span><span class="w">
</span></span></span></code></pre></div></li>
</ol>
<hr>
<p>代码实例:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// Task.java</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Task</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Callable</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="nf">call</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;the Thread name is &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">getName</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">BlockingQueue</span><span class="o">&lt;</span><span class="n">Runnable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">queue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayBlockingQueue</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="n">10</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">ThreadPoolExecutor</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">10</span><span class="p">,</span><span class="w"> </span><span class="n">20</span><span class="p">,</span><span class="w"> </span><span class="n">30</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">,</span><span class="w"> </span><span class="n">queue</span><span class="p">,</span><span class="w"> </span><span class="n">Executors</span><span class="p">.</span><span class="na">defaultThreadFactory</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//提交任务</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Future</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="n">future</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">submit</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Task</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//阻塞式限时 1s 获取返回值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;the return value is &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">future</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">1</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="p">(</span><span class="n">TimeoutException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//超时取消任务</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">future</span><span class="p">.</span><span class="na">cancel</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">ExecutionException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">err</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;Task failed: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">e</span><span class="p">.</span><span class="na">getCause</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">InterruptedException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">interrupt</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">err</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;Main thread interrupted&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//关闭线程池 </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">finally</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">shutdown</span><span class="p">();</span><span class="w"> </span><span class="c1">// 禁止新任务</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 等待已提交任务完成</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">executor</span><span class="p">.</span><span class="na">awaitTermination</span><span class="p">(</span><span class="n">5</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">shutdownNow</span><span class="p">();</span><span class="w"> </span><span class="c1">// 取消正在执行的任务</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">InterruptedException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">shutdownNow</span><span class="p">();</span><span class="w"> </span><span class="c1">// 被中断时强制关闭</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 为什么要恢复中断状态?</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 因为 catch 住异常后,中断标志被清除了</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 如果上层代码也想知道 &#34;发生过中断&#34;,必须重新设置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">interrupt</span><span class="p">();</span><span class="w"> </span><span class="c1">// 恢复中断状态</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h4 id="几个注意点">几个注意点</h4>
<ol>
<li>
<p><code>cancel(boolean mayInterruptIfRunning)</code>
向任务发送 <code>Thread.interrupt()</code> 只有任务能够响应中断才有效</p>
<ul>
<li>当 bool 为 false 时不中断当前正在执行的任务,只暂停未开始执行的任务</li>
<li>当 bool 为 true 时会中断当前正在执行的任务
通用写法:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="nf">call</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">isInterrupted</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">//</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//注意:这不是修改上面的 Task,而是展示一个通用写法。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Task 瞬间执行完毕而且 return 不会循环检查,没有响应中断。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 在该例中不会被中断,因为没有可中断阻塞点和循环检查</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;the Thread name is &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">getName</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="p">(</span><span class="n">InterruptedException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">interrupt</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">err</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;thread interrupted&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div></li>
</ol>
<blockquote>
<p>可中断阻塞点是什么?
调用 thread.interrupt() 会:
将该线程的中断状态(interrupt status)设置为 true。
如果线程正处于 <!-- raw HTML omitted --> sleep()、wait()、join()<!-- raw HTML omitted --> 等阻塞状态,会立即抛出 InterruptedException,并清除中断状态(变回 false)。</p>
</blockquote>
<ol start="2">
<li>
<p>如何优雅关闭线程池</p>
<p>明晰 <code>executor.shutdownNow()</code> 与 <code>executor.shutdown()</code></p>
</li>
</ol>
<table>
<thead>
<tr>
<th>方法</th>
<th>shutdown()</th>
<th>shutdownNow()</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>线程池状态变化</strong></td>
<td>不允许新任务提交,但已提交的任务继续执行</td>
<td>不允许新任务提交,且试图停止正在执行的任务</td>
</tr>
<tr>
<td><strong>新任务提交</strong></td>
<td>拒绝(抛出 RejectedExecutionException)</td>
<td>拒绝(抛出 RejectedExecutionException)</td>
</tr>
<tr>
<td><strong>正在执行的任务</strong></td>
<td>允许正常执行完成</td>
<td>尝试中断(调用 thread.interrupt())</td>
</tr>
<tr>
<td><strong>等待队列中的任务</strong></td>
<td>会继续执行(队列任务会逐个取出执行)</td>
<td>不执行,直接从队列中移除并返回</td>
</tr>
<tr>
<td><strong>中断行为</strong></td>
<td>只中断 <strong>空闲</strong> 的线程(interruptIdleWorkers())</td>
<td>中断 <strong>所有</strong> 工作线程(interruptWorkers())</td>
</tr>
<tr>
<td><strong>返回值</strong></td>
<td>void(无返回值)</td>
<td>List <!-- raw HTML omitted -->:返回队列中尚未开始执行的任务列表</td>
</tr>
<tr>
<td><strong>适用场景</strong></td>
<td>希望程序正常结束,不丢失任何已提交的任务</td>
<td>需要尽快停止,愿意牺牲队列中的任务</td>
</tr>
<tr>
<td><strong>是否会等待任务结束</strong></td>
<td>是(线程池会等到所有任务执行完才彻底终止)</td>
<td>否(立即尝试停止)</td>
</tr>
</tbody>
</table>
<pre><code>四步走关闭线程池:
1. 先优雅关闭 `shutdown()` 放在最外层,确保执行
2. 等待一定时间 `if(!executor.awaitTermination(5, TimeUnit.SECONDS))`
3. 时间到后强行关闭 `shutdownNow()`
4. 包裹 2-3 catch 中断异常,防止意外退出未关闭线程池
</code></pre>
<hr>
<h3 id="实战示例">实战示例</h3>
<p><strong>多线程并发执行任务</strong>
思路:</p>
<ol>
<li>
<p>创建数组或 list</p>
</li>
<li>
<p>将任务通过 for 循环提交到线程池中</p>
</li>
<li>
<p>查看结果</p>
</li>
</ol>
<p>框架:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">//1.创建线程池</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//2.创建待执行的任务队列 </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">List</span><span class="o">&lt;</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">futures</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//将任务提交到线程池</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">n</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">futures</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">executor</span><span class="p">.</span><span class="na">submit</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Task</span><span class="p">()));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">Future</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="n">future</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">futures</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//查看结果</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> </span><span class="k">finally</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//关闭线程池</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><table>
<thead>
<tr>
<th>操作</th>
<th>关键代码</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>定义任务</strong></td>
<td>implements Callable <!-- raw HTML omitted --></td>
<td>有返回值,能抛异常</td>
</tr>
<tr>
<td><strong>提交任务</strong></td>
<td>Future <!-- raw HTML omitted --> f = exec.submit(task)</td>
<td>异步,不阻塞</td>
</tr>
<tr>
<td><strong>获取结果</strong></td>
<td>f.get(1, TimeUnit.SECONDS)</td>
<td><strong>阻塞</strong>,建议设超时</td>
</tr>
<tr>
<td><strong>取消任务</strong></td>
<td>f.cancel(true)</td>
<td>需要任务代码配合响应中断</td>
</tr>
<tr>
<td><strong>大忌</strong></td>
<td>在 submit 循环中直接 get</td>
<td>会变成串行</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="使用现代的-completablefuture-完成多线程任务">使用现代的 CompletableFuture 完成多线程任务</h2>
<h3 id="快速上手-1">快速上手</h3>
<ol>
<li>创建线程池</li>
<li>创建逻辑(与 Future 不同,要实现 Supplier 接口的 get() 方法)</li>
<li>使用 CompletableFuture 提交任务</li>
<li>添加你要的操作
代码实例:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">try</span><span class="w"> </span><span class="p">(</span><span class="n">ThreadPoolExecutor</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">10</span><span class="p">,</span><span class="w"> </span><span class="n">20</span><span class="p">,</span><span class="w"> </span><span class="n">30</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">,</span><span class="w"> </span><span class="n">queue</span><span class="p">,</span><span class="w"> </span><span class="n">Executors</span><span class="p">.</span><span class="na">defaultThreadFactory</span><span class="p">()))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">CompletableFuture</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">CFTask</span><span class="p">(),</span><span class="w"> </span><span class="n">executor</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Long</span><span class="w"> </span><span class="n">long1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cf</span><span class="p">.</span><span class="na">join</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">long1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// close thread pool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>supplyAsync() 异步非阻塞提交任务</li>
</ul>
<table>
<thead>
<tr>
<th>方法</th>
<th>阻塞?</th>
<th>异常处理</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>get()</code></td>
<td>✅ 阻塞</td>
<td>抛 checked exception(必须 try-catch)</td>
</tr>
<tr>
<td><code>join()</code></td>
<td>✅ 阻塞</td>
<td>抛 unchecked <code>CompletionException</code>(可不 catch)</td>
</tr>
</tbody>
</table>
<hr>
<h3 id="常见的几种方法">常见的几种方法</h3>
<h4 id="创建异步任务">创建异步任务</h4>
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>方法</td>
<td>作用</td>
<td>例子</td>
</tr>
<tr>
<td>supplyAsync(Supplier, Executor)</td>
<td>指定线程池执行,返回有结果的异步线程</td>
<td>cf = CompletableFuture.supplyAsync(() -&gt; dbQuery(), myExecutor);</td>
</tr>
<tr>
<td>runAsync(Runnable, Executor)</td>
<td>无返回值异步任务,指定线程池</td>
<td>CompletableFuture.runAsync(() -&gt; sendEmail());</td>
</tr>
</tbody>
</table>
<ol>
<li>supplyAsync 异步执行要有返回值,被执行的任务要去实现 Supplier 接口</li>
<li>runAsync 异步执行但无返回值,要实现 Runnable 接口</li>
</ol>
<hr>
<h4 id="转换结果链式处理的核心">转换结果(链式处理的核心)</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
<th>例子</th>
</tr>
</thead>
<tbody>
<tr>
<td>thenApply(fn)</td>
<td>获取上一方法的返回值并执行逻辑,再次返回</td>
<td>.thenApply(user -&gt; user.getName())</td>
</tr>
<tr>
<td>thenApplyAsync(fn)</td>
<td>异步转换(新线程)</td>
<td>常用</td>
</tr>
<tr>
<td>thenCompose(fn)</td>
<td>上一步返回另一个 CF → 扁平化(避免嵌套)</td>
<td>.thenCompose(id -&gt; getOrderCF(id))</td>
</tr>
<tr>
<td>thenComposeAsync(fn)</td>
<td>异步扁平化</td>
<td>常用</td>
</tr>
</tbody>
</table>
<p><strong>thenApply vs thenCompose</strong>:</p>
<ul>
<li>thenApply:函数返回普通值</li>
<li>thenCompose:函数返回 CompletableFuture(必须用这个才能链下去不嵌套)</li>
</ul>
<hr>
<h4 id="消费结果只关心结果不返回新值">消费结果(只关心结果,不返回新值)</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
<th>例子</th>
</tr>
</thead>
<tbody>
<tr>
<td>thenAccept(result -&gt; &hellip;)</td>
<td>拿到结果处理(比如打印、存库)</td>
<td>.thenAccept(pname -&gt; System.out.println(pname))</td>
</tr>
<tr>
<td>thenAcceptAsync(&hellip;)</td>
<td>异步消费</td>
<td></td>
</tr>
<tr>
<td>thenRun(() -&gt; &hellip;)</td>
<td>不关心上一步结果,只做后续动作</td>
<td>.thenRun(() -&gt; {System.out.println(&ldquo;flushing&rdquo;);}</td>
</tr>
</tbody>
</table>
<hr>
<h4 id="异常处理">异常处理</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>exceptionally(ex -&gt; defaultValue)</td>
<td>出异常时返回备用值(继续链)</td>
</tr>
<tr>
<td>handle((result, ex) -&gt; &hellip;)</td>
<td>无论正常还是异常都能处理,最灵活</td>
</tr>
<tr>
<td>whenComplete((result, ex) -&gt; &hellip;)</td>
<td>只观察,不改变结果(常用于日志)</td>
</tr>
<tr>
<td>推荐优先用 handle 或 exceptionally。</td>
<td></td>
</tr>
</tbody>
</table>
<hr>
<h4 id="两个任务组合">两个任务组合</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>thenCombine(otherCF, (r1, r2) -&gt; newResult)</td>
<td>两个任务都完成 → 合并结果</td>
</tr>
<tr>
<td>thenCombineAsync(&hellip;)</td>
<td>异步合并</td>
</tr>
<tr>
<td>thenAcceptBoth(otherCF, (r1, r2) -&gt; &hellip;)</td>
<td>两个完成 → 消费,不返回</td>
</tr>
<tr>
<td>runAfterBoth(otherCF, () -&gt; &hellip;)</td>
<td>两个完成 → 执行动作</td>
</tr>
</tbody>
</table>
<hr>
<h4 id="多个任务全部完成">多个任务全部完成</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>CompletableFuture.allOf(cf1, cf2, cf3&hellip;)</td>
<td>等待所有任务完成,返回 void 的 CF</td>
</tr>
<tr>
<td>CompletableFuture.anyOf(cf1, cf2&hellip;)</td>
<td>哪个最快完成就返回那个结果</td>
</tr>
<tr>
<td>等待 n 个任务方式:</td>
<td></td>
</tr>
<tr>
<td><code>CompletableFuture.allof(futures.toArray(CompletableFuture[]::new)).join();</code> 或者</td>
<td></td>
</tr>
<tr>
<td><code>CompletableFuture.allof(futures.toArray(CompletableFuture[0])).join();</code></td>
<td></td>
</tr>
</tbody>
</table>
<blockquote>
<p>为什么要加 join()?
为了 <strong>阻塞等待所有任务完成</strong>, 如果不加 join 他会异步执行后续代码,未等待所有线程完成。</p>
</blockquote>
<p><img src="https://asgpipo.github.io/pics/Java/whyNeedJoin.png" alt="whyNeedJoin.png"></p>
<hr>
<h3 id="实战示例-1">实战示例</h3>
<ol>
<li>
<p>练习 supplyAsync (发起) thenApply (加工) thenCombine(合并) exceptionally(异常处理) thenAccept (最终消费)
已有</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">queryDB</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">queryName</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">unsafeApi</span><span class="p">()</span><span class="w">
</span></span></span></code></pre></div><p>目标:</p>
<ul>
<li>使用 <code>queryDB</code> 查询 username 查到结果后加入前缀 &ldquo;Proccessed_&rdquo;, 打印</li>
<li>使用 <code>queryDB</code> 查询 UserStats 和 OrderCounts 完成后组合结果合并成一个字符串 &ldquo;UserStats + OrderCounts&rdquo; 并打印</li>
<li>调用 <code>unsafeApi()</code> 如果发生错误,进行处理并且返回默认值 &ldquo;Default_Safe_Data&rdquo;</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">//task.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">queryDB</span><span class="p">(</span><span class="s">&#34;username&#34;</span><span class="p">),</span><span class="w"> </span><span class="n">executor</span><span class="p">)</span><span class="w"> </span><span class="c1">//{ return queryDB(&#34;username&#34;);} </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//此处加入 executor,后续会自动沿用这个线程,如果不使用 executor 会使用 forkjoinpool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">thenApply</span><span class="p">(</span><span class="n">res</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//自己命名接收到的值然后进行处理</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Proccessed_&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">res</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">thenAccept</span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">name</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//task.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">CompletableFuture</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cf1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">queryDB</span><span class="p">(</span><span class="s">&#34;UserStats&#34;</span><span class="p">),</span><span class="w"> </span><span class="n">executor</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">CompletableFuture</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cf2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">queryDB</span><span class="p">(</span><span class="s">&#34;OrderCounts&#34;</span><span class="p">),</span><span class="w"> </span><span class="n">executor</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">cf1</span><span class="p">.</span><span class="na">thenCombine</span><span class="p">(</span><span class="n">cf2</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">cf1</span><span class="p">,</span><span class="w"> </span><span class="n">cf2</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// thenCombine 会自动等待两个任务完成,不需要加 join() 或 get() 来强制等待</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">cf1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">cf2</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">.</span><span class="na">thenAccept</span><span class="p">(</span><span class="n">combined</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">log</span><span class="p">(</span><span class="s">&#34;合并报表: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">combined</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">queryDB</span><span class="p">(</span><span class="s">&#34;UserStats&#34;</span><span class="p">),</span><span class="w"> </span><span class="n">executor</span><span class="p">).</span><span class="na">thenCombine</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">//task.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">unsafeApi</span><span class="p">(),</span><span class="w"> </span><span class="n">executor</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">exceptionally</span><span class="p">((</span><span class="n">error</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">log</span><span class="p">(</span><span class="s">&#34;error: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">error</span><span class="p">.</span><span class="na">getMessage</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Default_Safe_Data&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">thenAccept</span><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">data</span><span class="p">));</span><span class="w">
</span></span></span></code></pre></div></li>
</ol>
<blockquote>
<p>⚠️ 注意</p>
<ul>
<li>thenApply 必须返回对象,不能是 void(比如只 println 不 return)。</li>
<li>异步异常必须处理,否则链会断掉</li>
</ul>
</blockquote>
<hr>
<ol start="2">
<li>
<p>练习 allOf(Future&hellip; cfs) (聚合多任务), completeOnTimeout() (超时控制)
已有</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="nf">fetchPrice</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">platform</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">batchQuery</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">platforms</span><span class="p">){}</span><span class="w">
</span></span></span></code></pre></div><p>目的:</p>
<ul>
<li>在 batchQuery() 中并发查询所有 platform</li>
<li>查询时长超过 2s 与错误的 返回 -1</li>
<li>等待所有任务完成后,获取数据返回 <code>List&lt;Double&gt;</code></li>
</ul>
</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">batchQuery</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">platforms</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">ExecutorService</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Executors</span><span class="p">.</span><span class="na">newFixedThreadPool</span><span class="p">(</span><span class="n">platforms</span><span class="p">.</span><span class="na">size</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">.</span><span class="na">0</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">CompletableFuture</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">futures</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w"> </span><span class="c1">//创建数组用来查询和接收 N 个平台的数据</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">platform</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">platforms</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">futures</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">supplyAsync</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">fetchPrice</span><span class="p">(</span><span class="n">platform</span><span class="p">),</span><span class="w"> </span><span class="n">executor</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">exceptionally</span><span class="p">((</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">err</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">platform</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">e</span><span class="p">.</span><span class="na">getMessage</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">.</span><span class="na">0</span><span class="p">;</span><span class="w"> </span><span class="c1">//报错,将异常吞掉,返回 -1 作为默认值</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}).</span><span class="na">completeOnTimeout</span><span class="p">(</span><span class="n">defaultValue</span><span class="p">,</span><span class="w"> </span><span class="n">2</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">));</span><span class="w"> </span><span class="c1">//(返回值, 时间, 单位)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//这里编排 有一些讲究</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//❌ ~~如果 completeOnTimeout 在 exceptionally 前,exceptionally 会接收到超时的报错~~ </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//✅ exceptionally:会吞掉真实的异常,返回正常值。 </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// completeOnTimeout:不会抛异常,但它吞掉的不是“异常”,而是“超时未完成”的状态,直接转为正常完成 + 默认值。</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">//⚠️ 着重 必须加 join 存放任务方式是 cf.toArray(CompletableFuture []::new)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">CompletableFuture</span><span class="p">.</span><span class="na">allOf</span><span class="p">(</span><span class="n">futures</span><span class="p">.</span><span class="na">toArray</span><span class="p">(</span><span class="n">CompletableFuture</span><span class="o">[]</span><span class="p">::</span><span class="k">new</span><span class="p">)).</span><span class="na">join</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">CompletableFuture</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="n">future</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">futures</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">result</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">future</span><span class="p">.</span><span class="na">join</span><span class="p">());</span><span class="w"> </span><span class="c1">// 这里拿真正的结果</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">result</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">finally</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">shutdown</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">executor</span><span class="p">.</span><span class="na">awaitTermination</span><span class="p">(</span><span class="n">5</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">shutdownNow</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">InterruptedException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">executor</span><span class="p">.</span><span class="na">shutdownNow</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Thread</span><span class="p">.</span><span class="na">currentThread</span><span class="p">().</span><span class="na">interrupt</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><blockquote>
<p>⚠️ 注意
<code>CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();</code>
返回值是 void 需要手动对单个任务获取
<code>completeOnTimeout</code>:不会抛异常,但它吞掉的不是“异常”,而是“超时未完成”的状态,直接转为正常完成 + 默认值。</p>
</blockquote>
<hr>
<h2 id="reference">Reference</h2>
<p><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html">ExecutorService (Java SE 17 &amp; JDK 17)</a></p>
<p><a href="https://www.baeldung.com/java-completablefuture?referrer=grok.com#bd-CompletableFuture-1">Guide To CompletableFuture | Baeldung</a></p>
</description>
</item>
<item>
<title>Java基础IO</title>
<link>https://asgpipo.github.io/posts/java/java_base/io/java-%E5%9F%BA%E7%A1%80-io/</link>
<pubDate>Sat, 27 Dec 2025 21:47:35 +0800</pubDate>
<guid>https://asgpipo.github.io/posts/java/java_base/io/java-%E5%9F%BA%E7%A1%80-io/</guid>
<description><p>modified-time: 2026-01-06 21:07:23</p>
<h2 id="字节流和字符流">字节流和字符流</h2>
<ul>
<li>字符流文件: .txt、.json、.csv、.xml 等</li>
<li>二进制文件(字节流):图片、视频、.pdf、.docx、.jar 等</li>
</ul>
<blockquote>
<p>本质上都是字节,只是字符流通过字符集编码,二进制文件不能直接当成文本去解码</p>
</blockquote>
<h3 id="读写字符流">读写字符流</h3>
<ol>
<li>
<p>处理流</p>
<ul>
<li><code>BufferedReader</code> 和 <code>BufferedWriter</code> 处理读取到的文件。</li>
</ul>
</li>
<li>
<p>转换流</p>
<ul>
<li>使用 <code>InputStreamReader</code> 和 <code>OutputStreamWriter</code> 将读取到的字节流转换为正确编码的 字符流 需要指定明确的编码 可以直接字符串也可以使用 <code>StandardCharsets.xxx</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">new</span><span class="w"> </span><span class="n">InputStreamReader</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">FileInputStream</span><span class="p">(</span><span class="n">inputFile</span><span class="p">),</span><span class="w"> </span><span class="n">StandardCharsets</span><span class="p">.</span><span class="na">UTF_8</span><span class="w"> </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">new</span><span class="w"> </span><span class="n">InputStreamReader</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">FileInputStream</span><span class="p">(</span><span class="n">inputFile</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;UTF-8&#34;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div></li>
</ol>
<blockquote>
<p>为什么不使用 <strong>FileReader</strong> 和 <strong>FileWriter</strong>?
new FileReader(filePath) // 内部实际是 new InputStreamReader(new FileInputStream(&hellip;), 平台默认编码)
他们内部使用的是当前平台默认编码,无法显式指定读取写入的编码方式。
<strong>结论:永远不要用 FileReader / FileWriter,改用 FileInputStream + InputStreamReader(指定编码)</strong></p>
</blockquote>
<ol start="3">
<li>节点流 <code>FileInputStream</code> 和 <code>FileOutputSteam</code> 直接读取</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// 示例</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">try</span><span class="w"> </span><span class="p">(</span><span class="n">BufferedReader</span><span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BufferedReader</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">InputStreamReader</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">FileInputStream</span><span class="p">(</span><span class="n">inputFile</span><span class="p">),</span><span class="w"> </span><span class="n">StandardCharsets</span><span class="p">.</span><span class="na">UTF_8</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">BufferedWriter</span><span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BufferedWriter</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">OutputStreamWriter</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">FileOutputStream</span><span class="p">(</span><span class="n">outputFile</span><span class="p">),</span><span class="n">StandardCharsets</span><span class="p">.</span><span class="na">UTF_8</span><span class="p">)))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// do sth...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">IOException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">e</span><span class="p">.</span><span class="na">printStackTrace</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><hr>
<h3 id="读写字节流二进制">读写字节流(二进制)</h3>
<ol>
<li>处理流
<ul>
<li><code>BufferedInputStream</code> 和 <code>BufferedOutputStream</code></li>
<li>数据类型读写:<code>DataInputStream</code>、<code>DataOutputStream</code>(读写 int、double 等基本类型)</li>
<li>对象序列化:<code>ObjectInputStream</code>、<code>ObjectOutputStream</code></li>
</ul>
</li>
<li>转换流
(因为是二进制文件所以可以不使用转换流)</li>
<li>字节流