-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
459 lines (440 loc) · 130 KB
/
index.html
File metadata and controls
459 lines (440 loc) · 130 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
<!doctype html>
<html lang="zh"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><meta><title>w1m's Blog</title><link rel="manifest" href="/manifest.json"><meta name="application-name" content="w1m"><meta name="msapplication-TileImage" content="/img/favicon.svg"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-title" content="w1m"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta property="og:type" content="blog"><meta property="og:title" content="w1m's Blog"><meta property="og:url" content="http://example.com/"><meta property="og:site_name" content="w1m's Blog"><meta property="og:locale" content="zh_CN"><meta property="og:image" content="http://example.com/img/og_image.png"><meta property="article:author" content="w1m024"><meta property="twitter:card" content="summary"><meta property="twitter:image:src" content="http://example.com/img/og_image.png"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://example.com"},"headline":"w1m's Blog","image":["http://example.com/img/og_image.png"],"author":{"@type":"Person","name":"w1m024"},"publisher":{"@type":"Organization","name":"w1m's Blog","logo":{"@type":"ImageObject","url":"http://example.com/img/logo.svg"}},"description":""}</script><link rel="icon" href="/img/favicon.svg"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.0.0/css/all.css"><link data-pjax rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/styles/atom-one-light.css"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;600&family=Source+Code+Pro"><link data-pjax rel="stylesheet" href="/css/default.css"><style>body>.footer,body>.navbar,body>.section{opacity:0}</style><!--!--><!--!--><!--!--><!--!--><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cookieconsent@3.1.1/build/cookieconsent.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lightgallery@1.10.0/dist/css/lightgallery.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/justifiedGallery@3.8.1/dist/css/justifiedGallery.min.css"><!--!--><!--!--><!--!--><style>.pace{-webkit-pointer-events:none;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pace-inactive{display:none}.pace .pace-progress{background:#3273dc;position:fixed;z-index:2000;top:0;right:100%;width:100%;height:2px}</style><script src="https://cdn.jsdelivr.net/npm/pace-js@1.2.4/pace.min.js"></script><!--!--><!--!--><!-- hexo injector head_end start --><script>
(function () {
function switchTab() {
if (!location.hash) {
return;
}
const id = '#' + CSS.escape(location.hash.substring(1));
const $tabMenu = document.querySelector(`.tabs a[href="${id}"]`);
if (!$tabMenu) {
return;
}
const $tabMenuContainer = $tabMenu.parentElement.parentElement;
Array.from($tabMenuContainer.children).forEach($menu => $menu.classList.remove('is-active'));
Array.from($tabMenuContainer.querySelectorAll('a'))
.map($menu => document.getElementById($menu.getAttribute("href").substring(1)))
.forEach($content => $content.classList.add('is-hidden'));
if ($tabMenu) {
$tabMenu.parentElement.classList.add('is-active');
}
const $activeTab = document.querySelector(id);
if ($activeTab) {
$activeTab.classList.remove('is-hidden');
}
}
switchTab();
window.addEventListener('hashchange', switchTab, false);
})();
</script><!-- hexo injector head_end end --><meta name="generator" content="Hexo 7.3.0"></head><body class="is-2-column"><nav class="navbar navbar-main"><div class="container navbar-container"><div class="navbar-brand justify-content-center"><a class="navbar-item navbar-logo" href="/"><img src="/img/logo.svg" alt="w1m's Blog" height="28"></a></div><div class="navbar-menu"><div class="navbar-start"><a class="navbar-item is-active" href="/">Home</a><a class="navbar-item" href="/archives">Archives</a><a class="navbar-item" href="/categories">Categories</a><a class="navbar-item" href="/tags">Tags</a><a class="navbar-item" href="/about">About</a></div><div class="navbar-end"><a class="navbar-item" target="_blank" rel="noopener" title="Download on GitHub" href="https://github.com/ppoffice/hexo-theme-icarus"><i class="fab fa-github"></i></a><a class="navbar-item search" title="搜索" href="javascript:;"><i class="fas fa-search"></i></a></div></div></div></nav><section class="section"><div class="container"><div class="columns"><div class="column order-2 column-main is-8-tablet is-8-desktop is-8-widescreen"><div class="card"><article class="card-content article" role="article"><div class="article-meta is-size-7 is-uppercase level is-mobile"><div class="level-left"><span class="level-item"><time dateTime="2025-06-20T08:08:54.000Z" title="2025/6/20 16:08:54">2025-06-20</time>发表</span><span class="level-item"><time dateTime="2025-06-20T08:11:40.286Z" title="2025/6/20 16:11:40">2025-06-20</time>更新</span><span class="level-item">10 分钟读完 (大约1558个字)</span></div></div><p class="title is-3 is-size-4-mobile"><a class="link-muted" href="/2025/06/20/%E9%81%93%E6%99%AE%E4%BA%91%E9%9D%A2%E8%AF%95/">道普云面试</a></p><div class="content"><ul>
<li>自我介绍</li>
</ul>
<hr>
<ul>
<li>java基础: List了解,ArrayList底层实现,延伸出扩容机制;Hashmap底层实现;<br>答出了ArrayList动态数组实现,扩容机制(Arrays.copyOf创建新数组并迁移)。问到具体的容量和扩容倍数没有答上来。<br>Hashmap底层什么实现的提示后说了数组加链表;实际上是数组+链表,链表长度≥8且数组长度≥64时,链表转红黑树。没有问Hashmap扩容,看来是看这个样子我也答不上来。</li>
</ul>
<blockquote>
<p> <strong>HashMap扩容机制</strong></p>
</blockquote>
<p> <strong>触发条件:当 <code>HashMap</code> 中的元素数量(<code>size</code>)超过 <code>capacity * loadFactor</code> 时触发扩容。</strong></p>
<ul>
<li><code>capacity</code>:当前数组(<code>Node<K,V>[] table</code>)的长度(默认初始 <code>16</code>)</li>
<li><code>loadFactor</code>:负载因子(默认 <code>0.75</code>)<br> <strong>扩容过程</strong></li>
</ul>
<ol>
<li><strong>计算新容量:</strong><code>新容量 = 旧容量 << 1</code>(最大容量<code>1 << 30</code>)</li>
<li><strong>数据迁移:</strong> 遍历旧数组,重新计算每个键值对的哈希值,并分配到新数组的对应位置 。由于新容量是 <code>2 倍</code>,元素在新数组的位置要么是 <code>原位置</code>,要么是 <code>原位置 + 旧容量</code>。</li>
<li><strong>链表拆分:</strong> 如果某个桶是链表,会拆分成两个链表(<code>low</code> 和 <code>high</code>),分别放在新数组的 <code>原位置</code> 和 <code>原位置 + 旧容量</code></li>
<li><strong>树形优化:</strong> 如果某个桶已经是红黑树,会检查是否需要拆分成两棵树或退化为链表。</li>
</ol>
<hr>
<ul>
<li>商城项目为什么要用redis缓存商品数据和营业状态数据,被纠正不能把Redis当作DB,因为持久化。讲了一些RDBAOF持久化的机制,询问有没有了解过mysql和Redis如何保持数据一致性;<blockquote>
<p><strong>为什么用Redis</strong></p>
</blockquote>
</li>
<li><strong>商品数据</strong>:高频读取,缓存减轻MySQL压力,响应速度提升。</li>
<li><strong>营业状态</strong>:需要快速切换(如打烊状态),Redis的原子操作(如<code>SETNX</code>)更高效。<blockquote>
<p><strong>持久化问题</strong></p>
</blockquote>
</li>
<li><strong>RDB</strong>:定时快照,适合备份但可能丢失最近数据。</li>
<li><strong>AOF</strong>:记录写命令,可配置为<code>everysec</code>平衡性能与安全。<blockquote>
<p><strong>数据一致性方案</strong></p>
</blockquote>
</li>
<li><strong>双写</strong>:先更新DB再删缓存(存在短暂不一致)。</li>
<li><strong>监听Binlog</strong>:通过Canal将MySQL变更同步到Redis(最终一致性)</li>
</ul>
<hr>
<ul>
<li>问mysql索引底层实现的数据结构,有哪些情况会导致索引失效,mysql调优相关<br>mysql索引答描述性的了B+树,突然想不起来链接叶子节点的叫双向链表,描述了很久。索引失效、调优都说了一点《数据库原理》课上讲的内容。<br>参考:<br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/dgp-zjz/p/18743672">mysql 索引失效及其解决办法 - 凡人半睁眼 - 博客园</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lgx211/p/18499524">一文彻底弄懂MySQL的优化 - lgx211 - 博客园</a></li>
</ul>
<hr>
<ul>
<li>问Spring常用注解,配置类用哪个注解<br>答得很磕绊,所以被直接问配置类注解是什么,答了@Bean,提示着说出了@Configuration。</li>
</ul>
<blockquote>
<p>Spring常用注解</p>
</blockquote>
<ul>
<li><strong>常用注解</strong>:<ul>
<li><code>@Controller</code>/<code>@RestController</code>:定义Web层。</li>
<li><code>@Service</code>:业务逻辑层。</li>
<li><code>@Autowired</code>:依赖注入。</li>
<li><code>@Transactional</code>:声明事务。</li>
</ul>
</li>
<li><strong>配置类注解</strong>:<code>@Configuration</code> + <code>@Bean</code>。<br><code>@Configuration</code> 是一个类级别的注解,用于标识一个类作为 Spring 应用上下文的配置源。<code>@Bean</code> 是一个方法级别的注解,用于指示方法返回一个由 Spring 容器管理的 bean。</li>
</ul>
<hr>
<ul>
<li><p>询问rvos是微内核还是宏内核;具体实现了哪些功能;有没有设计虚拟内存还是直接映射的物理内存;进程调度有没有优先级;<br>实际上根本不知道微内核和宏内核的区别,被教了很久。其他就是照常交代情况。</p>
<blockquote>
<p>微内核与宏内核</p>
</blockquote>
</li>
<li><p><strong>宏内核:</strong> 简单来说,就是把很多东西都集成进内核,例如linux内核,除了最基本的进程、线程管理、内存管理外,文件系统,驱动,网络协议等等都在内核里面。优点是效率高。缺点是稳定性差,开发过程中的bug经常会导致整个系统挂掉。</p>
</li>
<li><p><strong>微内核:</strong> 内核中只有最基本的调度、内存管理。驱动、文件系统等都是用户态的守护进程去实现的。优点是超级稳定,驱动等的错误只会导致相应进程死掉,不会导致整个系统都崩溃,做驱动开发时,发现错误,只需要kill掉进程,修正后重启进程就行了,比较方便。缺点是效率低。典型代表QNX,QNX的文件系统是跑在用户态的进程,称为resmgr的东西,是订阅发布机制,文件系统的错误只会导致这个守护进程挂掉。不过数据吞吐量就比较不乐观了。</p>
</li>
</ul>
<hr>
<ul>
<li>OpenEuler适配的时候是否需要修改Doris源码;实习的时候TPC-H数据集怎么生成的;如何对比在x86和RISC-V上的表现;base64优化具体做了什么;<br>照着简历答了些东西,似乎感觉有点浅了。</li>
</ul>
<hr>
<ul>
<li>有没有了解过HTTPS加密算法<br>没有了解过。<blockquote>
<p>HTTPS加密原理<br><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/2523484">【网络编程】九、详解 HTTPS 加密原理-腾讯云开发者社区-腾讯云</a></p>
</blockquote>
</li>
</ul>
<hr>
<ul>
<li>对shell了解如何<br>说偶尔会用到shell脚本配置环境。</li>
</ul>
<hr>
<ul>
<li>AI专业作业一般做的是什么相关的。<br>没多讲,就说了下一些课堂任务。</li>
</ul>
<hr>
<ul>
<li>有没有用过matlab<br>说自己数学建模比赛用过。</li>
</ul>
</div></article></div><div class="card"><article class="card-content article" role="article"><div class="article-meta is-size-7 is-uppercase level is-mobile"><div class="level-left"><span class="level-item"><time dateTime="2025-06-08T08:51:36.000Z" title="2025/6/8 16:51:36">2025-06-08</time>发表</span><span class="level-item"><time dateTime="2025-06-08T09:30:30.823Z" title="2025/6/8 17:30:30">2025-06-08</time>更新</span><span class="level-item">9 分钟读完 (大约1401个字)</span></div></div><p class="title is-3 is-size-4-mobile"><a class="link-muted" href="/2025/06/08/%E6%9B%B2%E9%80%9F%E7%A7%91%E6%8A%80%E7%AC%94%E8%AF%95/">曲速科技笔试</a></p><div class="content"><h2><span id="a-电话系统设计">A 电话系统设计</span></h2><p>Q:设计一个简单电话,0-9数字键,拨号/挂机键;拨号键接通电话后会变成挂机键。考虑到扩展性,如追加*按键,添加按键音(类图或伪代码)<br>A:<br>以下是AI给出的答案。把电话的不同状态抽象成独立类确实比起设置按键状态更易拓展。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.HashMap; </span><br><span class="line"><span class="keyword">import</span> java.util.Map; </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 状态接口 </span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">PhoneState</span> { </span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">handleDigit</span><span class="params">(<span class="type">char</span> digit)</span>; </span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">handleDial</span><span class="params">()</span>; </span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">handleStar</span><span class="params">()</span>; <span class="comment">// 扩展特殊键 </span></span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 具体状态实现 </span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ReadyState</span> <span class="keyword">implements</span> <span class="title class_">PhoneState</span> { </span><br><span class="line"> <span class="keyword">private</span> Phone phone; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">ReadyState</span><span class="params">(Phone phone)</span> { </span><br><span class="line"> <span class="built_in">this</span>.phone = phone; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleDigit</span><span class="params">(<span class="type">char</span> digit)</span> { </span><br><span class="line"> phone.setCurrentNumber(<span class="string">""</span> + digit); </span><br><span class="line"> phone.getSoundService().playTone(digit); </span><br><span class="line"> phone.setState(<span class="keyword">new</span> <span class="title class_">DialingState</span>(phone)); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleDial</span><span class="params">()</span> { </span><br><span class="line"> <span class="comment">// 待机状态按拨号键无响应 </span></span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleStar</span><span class="params">()</span> { </span><br><span class="line"> phone.getSoundService().playTone(<span class="string">'*'</span>); </span><br><span class="line"> <span class="comment">// 可扩展*键功能 </span></span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DialingState</span> <span class="keyword">implements</span> <span class="title class_">PhoneState</span> { </span><br><span class="line"> <span class="keyword">private</span> Phone phone; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">DialingState</span><span class="params">(Phone phone)</span> { </span><br><span class="line"> <span class="built_in">this</span>.phone = phone; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleDigit</span><span class="params">(<span class="type">char</span> digit)</span> { </span><br><span class="line"> phone.setCurrentNumber(phone.getCurrentNumber() + digit); </span><br><span class="line"> phone.getSoundService().playTone(digit); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleDial</span><span class="params">()</span> { </span><br><span class="line"> System.out.println(<span class="string">"Dialing: "</span> + phone.getCurrentNumber()); </span><br><span class="line"> phone.setState(<span class="keyword">new</span> <span class="title class_">ConnectedState</span>(phone)); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleStar</span><span class="params">()</span> { </span><br><span class="line"> phone.getSoundService().playTone(<span class="string">'*'</span>); </span><br><span class="line"> <span class="comment">// 可扩展*键功能 </span></span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ConnectedState</span> <span class="keyword">implements</span> <span class="title class_">PhoneState</span> { </span><br><span class="line"> <span class="keyword">private</span> Phone phone; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">ConnectedState</span><span class="params">(Phone phone)</span> { </span><br><span class="line"> <span class="built_in">this</span>.phone = phone; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleDigit</span><span class="params">(<span class="type">char</span> digit)</span> { </span><br><span class="line"> <span class="comment">// 通话中输入数字(如DTMF) </span></span><br><span class="line"> phone.getSoundService().playTone(digit); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleDial</span><span class="params">()</span> { </span><br><span class="line"> System.out.println(<span class="string">"Hanging up"</span>); </span><br><span class="line"> phone.setCurrentNumber(<span class="string">""</span>); </span><br><span class="line"> phone.setState(<span class="keyword">new</span> <span class="title class_">ReadyState</span>(phone)); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleStar</span><span class="params">()</span> { </span><br><span class="line"> phone.getSoundService().playTone(<span class="string">'*'</span>); </span><br><span class="line"> <span class="comment">// 可扩展*键功能 </span></span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 按键抽象 </span></span><br><span class="line"><span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Button</span> { </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">press</span><span class="params">()</span>; </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DigitButton</span> <span class="keyword">extends</span> <span class="title class_">Button</span> { </span><br><span class="line"> <span class="keyword">private</span> <span class="type">char</span> digit; </span><br><span class="line"> <span class="keyword">private</span> Phone phone; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">DigitButton</span><span class="params">(<span class="type">char</span> digit, Phone phone)</span> { </span><br><span class="line"> <span class="built_in">this</span>.digit = digit; </span><br><span class="line"> <span class="built_in">this</span>.phone = phone; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">press</span><span class="params">()</span> { </span><br><span class="line"> phone.getState().handleDigit(digit); </span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StarButton</span> <span class="keyword">extends</span> <span class="title class_">Button</span> { </span><br><span class="line"> <span class="keyword">private</span> Phone phone; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">StarButton</span><span class="params">(Phone phone)</span> { </span><br><span class="line"> <span class="built_in">this</span>.phone = phone; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">press</span><span class="params">()</span> { </span><br><span class="line"> phone.getState().handleStar(); </span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DialButton</span> <span class="keyword">extends</span> <span class="title class_">Button</span> { </span><br><span class="line"> <span class="keyword">private</span> Phone phone; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">DialButton</span><span class="params">(Phone phone)</span> { </span><br><span class="line"> <span class="built_in">this</span>.phone = phone; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">press</span><span class="params">()</span> { </span><br><span class="line"> phone.getState().handleDial(); </span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 声音服务 </span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SoundService</span> { </span><br><span class="line"> <span class="keyword">private</span> Map<Character, String> tones = <span class="keyword">new</span> <span class="title class_">HashMap</span><>(); </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">SoundService</span><span class="params">()</span> { </span><br><span class="line"> <span class="comment">// 初始化音调映射 </span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">char</span> <span class="variable">c</span> <span class="operator">=</span> <span class="string">'0'</span>; c <= <span class="string">'9'</span>; c++) { </span><br><span class="line"> tones.put(c, <span class="string">"DTMF-"</span> + c); </span><br><span class="line"> } </span><br><span class="line"> tones.put(<span class="string">'*'</span>, <span class="string">"DTMF-star"</span>); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">playTone</span><span class="params">(<span class="type">char</span> digit)</span> { </span><br><span class="line"> <span class="type">String</span> <span class="variable">tone</span> <span class="operator">=</span> tones.getOrDefault(digit, <span class="string">"default-tone"</span>); </span><br><span class="line"> System.out.println(<span class="string">"Playing: "</span> + tone); </span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 电话主体 </span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Phone</span> { </span><br><span class="line"> <span class="keyword">private</span> PhoneState state; </span><br><span class="line"> <span class="keyword">private</span> SoundService soundService; </span><br><span class="line"> <span class="keyword">private</span> <span class="type">String</span> <span class="variable">currentNumber</span> <span class="operator">=</span> <span class="string">""</span>; </span><br><span class="line"> <span class="keyword">private</span> Map<String, Button> buttons = <span class="keyword">new</span> <span class="title class_">HashMap</span><>(); </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Phone</span><span class="params">()</span> { </span><br><span class="line"> <span class="built_in">this</span>.soundService = <span class="keyword">new</span> <span class="title class_">SoundService</span>(); </span><br><span class="line"> <span class="built_in">this</span>.state = <span class="keyword">new</span> <span class="title class_">ReadyState</span>(<span class="built_in">this</span>); </span><br><span class="line"> initButtons(); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">initButtons</span><span class="params">()</span> { </span><br><span class="line"> <span class="comment">// 数字键 </span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i <= <span class="number">9</span>; i++) { </span><br><span class="line"> <span class="type">char</span> <span class="variable">digit</span> <span class="operator">=</span> (<span class="type">char</span>)(<span class="string">'0'</span> + i); </span><br><span class="line"> buttons.put(<span class="string">"DIGIT_"</span> + digit, <span class="keyword">new</span> <span class="title class_">DigitButton</span>(digit, <span class="built_in">this</span>)); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 特殊键 </span></span><br><span class="line"> buttons.put(<span class="string">"STAR"</span>, <span class="keyword">new</span> <span class="title class_">StarButton</span>(<span class="built_in">this</span>)); </span><br><span class="line"> buttons.put(<span class="string">"DIAL"</span>, <span class="keyword">new</span> <span class="title class_">DialButton</span>(<span class="built_in">this</span>)); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 状态管理方法 </span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setState</span><span class="params">(PhoneState state)</span> { </span><br><span class="line"> <span class="built_in">this</span>.state = state; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> PhoneState <span class="title function_">getState</span><span class="params">()</span> { </span><br><span class="line"> <span class="keyword">return</span> state; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> SoundService <span class="title function_">getSoundService</span><span class="params">()</span> { </span><br><span class="line"> <span class="keyword">return</span> soundService; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getCurrentNumber</span><span class="params">()</span> { </span><br><span class="line"> <span class="keyword">return</span> currentNumber; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCurrentNumber</span><span class="params">(String number)</span> { </span><br><span class="line"> <span class="built_in">this</span>.currentNumber = number; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 按键触发 </span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pressButton</span><span class="params">(String buttonId)</span> { </span><br><span class="line"> <span class="type">Button</span> <span class="variable">button</span> <span class="operator">=</span> buttons.get(buttonId); </span><br><span class="line"> <span class="keyword">if</span> (button != <span class="literal">null</span>) { </span><br><span class="line"> button.press(); </span><br><span class="line"> } </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 扩展新按钮 </span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addButton</span><span class="params">(String id, Button button)</span> { </span><br><span class="line"> buttons.put(id, button); </span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 使用示例 </span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> { </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> { </span><br><span class="line"> <span class="type">Phone</span> <span class="variable">phone</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Phone</span>(); </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 拨号示例 </span></span><br><span class="line"> phone.pressButton(<span class="string">"DIGIT_1"</span>); </span><br><span class="line"> phone.pressButton(<span class="string">"DIGIT_2"</span>); </span><br><span class="line"> phone.pressButton(<span class="string">"DIGIT_3"</span>); </span><br><span class="line"> phone.pressButton(<span class="string">"DIAL"</span>); <span class="comment">// 拨号 </span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 特殊键使用 </span></span><br><span class="line"> phone.pressButton(<span class="string">"STAR"</span>); </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 挂断 </span></span><br><span class="line"> phone.pressButton(<span class="string">"DIAL"</span>); <span class="comment">// 此时变为挂机键 </span></span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2><span id="b-字符串处理算法题">B 字符串处理算法题</span></h2><p>Q:算法题,找出字符串中出现次数最多的字符</p>
<p>A:<br>比较简单,时间复杂度$O(n)$。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MostFrequentChar</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> List<Character> <span class="title function_">findMostFrequentChar</span><span class="params">(String str)</span> {</span><br><span class="line"> <span class="keyword">if</span> (str == <span class="literal">null</span> || str.isEmpty()) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Map<Character, Integer> charCount = <span class="keyword">new</span> <span class="title class_">HashMap</span><>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">maxCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 统计字符频率并记录最大出现次数</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">char</span> c : str.toCharArray()) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> charCount.getOrDefault(c, <span class="number">0</span>) + <span class="number">1</span>;</span><br><span class="line"> charCount.put(c, count);</span><br><span class="line"> <span class="keyword">if</span> (count > maxCount) {</span><br><span class="line"> maxCount = count;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 找出所有出现次数等于maxCount的字符</span></span><br><span class="line"> List<Character> result = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="keyword">for</span> (Map.Entry<Character, Integer> entry : charCount.entrySet()) {</span><br><span class="line"> <span class="keyword">if</span> (entry.getValue() == maxCount) {</span><br><span class="line"> result.add(entry.getKey());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">"aabbc"</span>;</span><br><span class="line"> List<Character> result = findMostFrequentChar(str);</span><br><span class="line"> System.out.println(<span class="string">"Most frequent characters: "</span> + result);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2><span id="c-sql查询语句">C SQL查询语句</span></h2><p>Q: sql查询语句,查询2022年入职的所有用户信息并按照公司ID排序,包含用户表、用户关系表(用户和上级)、公司表<br>A: 作答的时候好像都没有好好看要求,回来后才想起来没排序。</p>
<h2><span id="d-多线程文件下载实现">D 多线程文件下载实现</span></h2><p>Q:给了<code>String[] url, String dir</code>下载N个文件到某个目录,文件名截取url后面一部分,完全下载完成后返回true;用不超过3个线程处理。<br>下载文件的方法不需要实现<code>publci byte[] getBytesFromUrl(String url);</code></p>
<p>A:<br>Java多线程部分需要进一步学习。<br>查看<a href="./java%E5%A4%9A%E7%BA%BF%E7%A8%8B.md">Java多线程笔记</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FileDownloader</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MAX_THREADS</span> <span class="operator">=</span> <span class="number">3</span>; <span class="comment">// 最大线程数</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">downloadFiles</span><span class="params">(String[] urls, String dir)</span> {</span><br><span class="line"> <span class="comment">// 检查目录是否存在,不存在则创建</span></span><br><span class="line"> <span class="type">File</span> <span class="variable">directory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(dir);</span><br><span class="line"> <span class="keyword">if</span> (!directory.exists() && !directory.mkdirs()) {</span><br><span class="line"> System.err.println(<span class="string">"无法创建目录: "</span> + dir);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建线程池(固定大小为3)</span></span><br><span class="line"> <span class="type">ExecutorService</span> <span class="variable">executor</span> <span class="operator">=</span> Executors.newFixedThreadPool(MAX_THREADS);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建异步任务列表</span></span><br><span class="line"> List<CompletableFuture<Boolean>> futures = </span><br><span class="line"> Arrays.stream(urls)</span><br><span class="line"> .map(url -> downloadAsync(url, dir, executor))</span><br><span class="line"> .collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 等待所有任务完成并收集结果</span></span><br><span class="line"> <span class="type">boolean</span> <span class="variable">allSuccess</span> <span class="operator">=</span> futures.stream()</span><br><span class="line"> .map(CompletableFuture::join)</span><br><span class="line"> .allMatch(success -> success);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 关闭线程池</span></span><br><span class="line"> executor.shutdown();</span><br><span class="line"> <span class="keyword">return</span> allSuccess;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> CompletableFuture<Boolean> <span class="title function_">downloadAsync</span><span class="params">(String url, String dir, ExecutorService executor)</span> {</span><br><span class="line"> <span class="keyword">return</span> CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 获取文件名(URL最后一个'/'后的部分)</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> url.substring(url.lastIndexOf(<span class="string">'/'</span>) + <span class="number">1</span>);</span><br><span class="line"> <span class="type">File</span> <span class="variable">outputFile</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(dir, fileName);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 获取文件内容(题目提供的方法)</span></span><br><span class="line"> <span class="type">byte</span>[] data = getBytesFromUrl(url);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 写入文件</span></span><br><span class="line"> <span class="keyword">try</span> (<span class="type">FileOutputStream</span> <span class="variable">fos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(outputFile)) {</span><br><span class="line"> fos.write(data);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> System.err.println(<span class="string">"下载失败: "</span> + url + <span class="string">" | 错误: "</span> + e.getMessage());</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }, executor);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 题目提供的下载方法(实际实现应处理网络请求)</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">byte</span>[] getBytesFromUrl(String url) <span class="keyword">throws</span> Exception {</span><br><span class="line"> <span class="comment">// 这里应包含实际的下载逻辑</span></span><br><span class="line"> <span class="comment">// 示例中我们返回空数组</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2><span id="e-服务器性能问题排查">E 服务器性能问题排查</span></h2><p>Q:你开发的功能模块,发布到生产后,服务器压力激增,怎么排查是什么原因造成的?<br>A:<br>参考以下文章:<br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/yanggb/p/10846806.html">服务器压力大的解决方案 - yanggb - 博客园</a><br><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1879120">服务器压力过大?CPU打满?我来帮你快速检查Linux服务器性能-腾讯云开发者社区-腾讯云</a></p>
</div></article></div><div class="card"><article class="card-content article" role="article"><div class="article-meta is-size-7 is-uppercase level is-mobile"><div class="level-left"><span class="level-item"><time dateTime="2025-06-08T07:11:55.000Z" title="2025/6/8 15:11:55">2025-06-08</time>发表</span><span class="level-item"><time dateTime="2025-06-08T08:50:57.092Z" title="2025/6/8 16:50:57">2025-06-08</time>更新</span><span class="level-item">34 分钟读完 (大约5075个字)</span></div></div><p class="title is-3 is-size-4-mobile"><a class="link-muted" href="/2025/06/08/java%E5%A4%9A%E7%BA%BF%E7%A8%8B/">java多线程笔记</a></p><div class="content"><!-- toc -->
<ul>
<li><a href="#%E5%88%9B%E5%BB%BA%E6%96%B0%E7%BA%BF%E7%A8%8B">创建新线程</a></li>
<li><a href="#%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81">线程的状态</a></li>
<li><a href="#%E4%B8%AD%E6%96%AD%E7%BA%BF%E7%A8%8B">中断线程</a></li>
<li><a href="#%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B">守护线程</a></li>
<li><a href="#%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5">线程同步</a><ul>
<li><a href="#%E5%90%8C%E6%AD%A5%E6%96%B9%E6%B3%95">同步方法</a></li>
<li><a href="#%E6%AD%BB%E9%94%81">死锁</a></li>
<li><a href="#wait%E5%92%8Cnotify">wait()和notify()</a></li>
<li><a href="#reentrantlock">ReentrantLock()</a></li>
<li><a href="#condition">Condition</a></li>
<li><a href="#readwritelock">ReadWriteLock</a></li>
<li><a href="#stampedlock">StampedLock</a></li>
<li><a href="#semphore">Semphore</a></li>
<li><a href="#javautilconcurrent%E5%8C%85%E6%8F%90%E4%BE%9B%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%9B%86%E5%90%88">java.util.concurrent包提供的线程安全集合</a></li>
<li><a href="#javautilconcurrentatomic%E5%8C%85%E6%8F%90%E4%BE%9B%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C">java.util.concurrent.atomic包提供原子操作</a></li>
</ul>
</li>
<li><a href="#%E4%BD%BF%E7%94%A8%E7%BA%BF%E7%A8%8B%E6%B1%A0">使用线程池</a></li>
<li><a href="#future">Future</a></li>
<li><a href="#completablefuture">CompletableFuture</a></li>
<li><a href="#forkjoin">ForkJoin</a></li>
<li><a href="#threadlocal">ThreadLocal</a></li>
<li><a href="#%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8B">虚拟线程</a></li>
</ul>
<!-- tocstop -->
<p>Java语言内置了多线程支持:一个Java程序实际上是一个JVM进程,JVM进程用一个主线程来执行<code>main()</code>方法,在<code>main()</code>方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。</p>
<h2><span id="创建新线程">创建新线程</span></h2><p><strong>方法1:</strong><br>Java用<code>Thread</code>对象表示一个线程,通过调用<code>start()</code>启动一个新线程;<br>一个线程对象只能调用一次<code>start()</code>方法;<br>线程的执行代码写在<code>run()</code>方法中;<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// 多线程</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br><span class="line"> t.start(); <span class="comment">// 启动新线程</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"start new thread!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> { </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> { </span><br><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">MyRunnable</span>()); </span><br><span class="line"> <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -> { </span><br><span class="line">System.out.println(<span class="string">"start new thread with lambda."</span>); </span><br><span class="line">}); </span><br><span class="line"> t1.start(); </span><br><span class="line"> t2.start(); </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">} </span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">MyRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> { </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> { </span><br><span class="line"> System.out.println(<span class="string">"start new thread."</span>); </span><br><span class="line"> } </span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p><strong>线程优先级</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Thread.setPriority(<span class="type">int</span> n) <span class="comment">// 1~10, 默认值5</span></span><br></pre></td></tr></table></figure>
<p>多线程经常需要读写共享数据,并且需要同步。</p>
<h2><span id="线程的状态">线程的状态</span></h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"> ┌─────────────┐</span><br><span class="line"> │ New │</span><br><span class="line"> └─────────────┘</span><br><span class="line"> │</span><br><span class="line"> ▼</span><br><span class="line">┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐</span><br><span class="line"> ┌─────────────┐ ┌─────────────┐</span><br><span class="line">││ Runnable │ │ Blocked ││</span><br><span class="line"> └─────────────┘ └─────────────┘</span><br><span class="line">│┌─────────────┐ ┌─────────────┐│</span><br><span class="line"> │ Waiting │ │Timed Waiting│</span><br><span class="line">│└─────────────┘ └─────────────┘│</span><br><span class="line"> ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─</span><br><span class="line"> │</span><br><span class="line"> ▼</span><br><span class="line"> ┌─────────────┐</span><br><span class="line"> │ Terminated │</span><br><span class="line"> └─────────────┘</span><br></pre></td></tr></table></figure>
<p> 线程状态转移图<br><strong>线程状态</strong>包含:</p>
<ul>
<li>New:新创建的线程,尚未执行;</li>
<li>Runnable:运行中的线程,正在执行<code>run()</code>方法的Java代码;</li>
<li>Blocked:运行中的线程,因为某些操作被阻塞而挂起;</li>
<li>Waiting:运行中的线程,因为某些操作在等待中;</li>
<li>Timed Waiting:运行中的线程,因为执行<code>sleep()</code>方法正在计时等待;</li>
<li>Terminated:线程已终止,因为<code>run()</code>方法执行完毕。</li>
</ul>
<p><strong>状态切换</strong></p>
<ul>
<li>通过对另一个线程对象调用<code>join()</code>方法可以等待其执行结束;</li>
<li>可以指定等待时间,超过等待时间线程仍然没有结束就不再等待;</li>
<li>对已经运行结束的线程调用<code>join()</code>方法会立刻返回。</li>
</ul>
<h2><span id="中断线程">中断线程</span></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 中断线程</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException {</span><br><span class="line"> <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br><span class="line"> t.start();</span><br><span class="line"> Thread.sleep(<span class="number">1</span>); <span class="comment">// 暂停1毫秒</span></span><br><span class="line"> t.interrupt(); <span class="comment">// 中断t线程</span></span><br><span class="line"> t.join(); <span class="comment">// 等待t线程结束</span></span><br><span class="line"> System.out.println(<span class="string">"end"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (! isInterrupted()) { <span class="comment">// 必要</span></span><br><span class="line"> n ++;</span><br><span class="line"> System.out.println(n + <span class="string">" hello!"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 捕捉Exception</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">Thread</span> <span class="variable">hello</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloThread</span>();</span><br><span class="line"> hello.start(); <span class="comment">// 启动hello线程</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> hello.join(); <span class="comment">// 等待hello线程结束</span></span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> System.out.println(<span class="string">"interrupted!"</span>);</span><br><span class="line"> }</span><br><span class="line"> hello.interrupt();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HelloThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (!isInterrupted()) {</span><br><span class="line"> n++;</span><br><span class="line"> System.out.println(n + <span class="string">" hello!"</span>);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">100</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>interrupt()</code>方法仅仅向<code>t</code>线程发出了“中断请求”,至于<code>t</code>线程是否能立刻响应,要看具体代码。而<code>t</code>线程的<code>while</code>循环会检测<code>isInterrupted()</code>,所以上述代码能正确响应<code>interrupt()</code>请求,使得自身立刻结束运行<code>run()</code>方法。<br>目标线程检测到<code>isInterrupted()</code>为<code>true</code>或者捕获了<code>InterruptedException</code>都应该立刻结束自身线程;</p>
<p><strong>设置标志位</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 中断线程</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException {</span><br><span class="line"> <span class="type">HelloThread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloThread</span>();</span><br><span class="line"> t.start();</span><br><span class="line"> Thread.sleep(<span class="number">1</span>);</span><br><span class="line"> t.running = <span class="literal">false</span>; <span class="comment">// 标志位置为false</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HelloThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">running</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (running) {</span><br><span class="line"> n ++;</span><br><span class="line"> System.out.println(n + <span class="string">" hello!"</span>);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"end!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>注意到<code>HelloThread</code>的标志位<code>boolean running</code>是一个线程间共享的变量。线程间共享变量需要使用<code>volatile</code>关键字标记,确保每个线程都能读取到更新后的变量值。<br>[[为什么要对线程间共享的变量用关键字volatile声明?]]<br><code>volatile</code>关键字的目的是告诉虚拟机:</p>
<ul>
<li>每次访问变量时,总是获取主内存的最新值;</li>
<li>每次修改变量后,立刻回写到主内存。<br><em><code>volatile</code>关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值。</em></li>
</ul>
<h2><span id="守护线程">守护线程</span></h2><p>守护线程(Daemon Thread)是指为其他线程服务的线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。<br><strong>创建守护线程</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyThread</span>();</span><br><span class="line">t.setDaemon(<span class="literal">true</span>); <span class="comment">// 标记</span></span><br><span class="line">t.start();</span><br></pre></td></tr></table></figure>
<p>守护线程不能持有需要关闭的资源(如打开文件等)。</p>
<h2><span id="线程同步">线程同步</span></h2><p>使用<code>synchronized</code>解决了多线程同步访问共享变量的正确性问题。但是,它的缺点是带来了性能下降。因为<code>synchronized</code>代码块无法并发执行。</p>
<p>我们来概括一下如何使用<code>synchronized</code>:</p>
<ol>
<li>找出修改共享变量的线程代码块;</li>
<li>选择一个共享实例作为锁;</li>
<li>使用<code>synchronized(lockObject) { ... }</code>。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 多线程</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line"> <span class="type">var</span> <span class="variable">add</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AddThread</span>();</span><br><span class="line"> <span class="type">var</span> <span class="variable">dec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DecThread</span>();</span><br><span class="line"> add.start();</span><br><span class="line"> dec.start();</span><br><span class="line"> add.join();</span><br><span class="line"> dec.join();</span><br><span class="line"> System.out.println(Counter.count);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Counter</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AddThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i<<span class="number">10000</span>; i++) {</span><br><span class="line"> <span class="keyword">synchronized</span>(Counter.lock) {</span><br><span class="line"> Counter.count += <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DecThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i<<span class="number">10000</span>; i++) {</span><br><span class="line"> <span class="keyword">synchronized</span>(Counter.lock) {</span><br><span class="line"> Counter.count -= <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>在使用<code>synchronized</code>的时候,不必担心抛出异常。因为无论是否有异常,都会在<code>synchronized</code>结束处正确释放锁:</p>
<p><strong>不需要synchronized</strong></p>
<p>JVM规范定义了几种原子操作:</p>
<ul>
<li>基本类型(<code>long</code>和<code>double</code>除外)赋值,例如:<code>int n = m</code>;</li>
<li>引用类型赋值,例如:<code>List<String> list = anotherList</code>。</li>
</ul>
<p>单条原子操作不需要同步<br>不可变对象无需同步</p>
<h3><span id="同步方法">同步方法</span></h3><p>如果一个类被设计为允许多线程正确访问,我们就说这个类就是“线程安全”的(thread-safe)<br>Java标准库的<code>java.lang.StringBuffer</code>也是线程安全的。<br>还有一些不变类,例如<code>String</code>,<code>Integer</code>,<code>LocalDate</code>,它们的所有成员变量都是<code>final</code>,多线程同时访问时只能读不能写,这些不变类也是线程安全的。<br>类似<code>Math</code>这些只提供静态方法,没有成员变量的类,也是线程安全的。无状态的方法天然线程安全,因为<strong>没有共享数据可被竞争</strong>。</p>
<p>[[synchronized static 方法的锁机制]]</p>
<h3><span id="死锁">死锁</span></h3><p>JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做<strong>可重入锁</strong>。<br>所以,获取锁的时候,不但要判断是否是第一次获取,还要记录这是第几次获取。每获取一次锁,记录+1,每退出<code>synchronized</code>块,记录-1,减到0的时候,才会真正释放锁。</p>
<p>两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,造成了双方无限等待下去,这就是死锁。<br>死锁发生后,没有任何机制能解除死锁,只能强制结束JVM进程。<br>**解决方式:**线程获取锁的顺序要一致。</p>
<h3><span id="wait和notify">wait()和notify()</span></h3><p><strong>wait()</strong><br><strong>JVM 源码中的定义</strong>:<br><code>wait()</code> 的本地方法实现在 HotSpot JVM 的 C++ 代码中(如 <code>objectMonitor.cpp</code>),核心逻辑围绕 <strong>对象监视器(ObjectMonitor)</strong> 展开。</p>
<ul>
<li><strong>关键步骤</strong>:<ol>
<li><strong>检查锁状态</strong>:确认当前线程持有对象的锁(通过 <code>ObjectMonitor::owner</code> 字段)。</li>
<li><strong>释放锁</strong>:通过 <code>ObjectMonitor::exit()</code> 释放锁,允许其他线程进入同步块。</li>
<li><strong>线程挂起</strong>:调用操作系统原语(如 <code>pthread_cond_wait</code> 或 <code>WaitForSingleObject</code>)将线程挂起,进入等待队列。</li>
<li><strong>被唤醒后重新竞争锁</strong>:当其他线程调用 <code>notify()</code>/<code>notifyAll()</code> 时,线程被移入竞争队列,重新尝试获取锁(通过 <code>ObjectMonitor::enter()</code>)<br>因此,只能在锁对象上调用<code>wait()</code>方法。</li>
</ol>
</li>
</ul>
<p><strong>notify()</strong></p>
<ul>
<li><strong>唤醒一个等待线程</strong>:<br> 当调用 <code>object.notify()</code> 时,会随机唤醒一个正在该对象上通过 <code>object.wait()</code> 阻塞的线程(如果有多个线程在等待)。</li>
<li><strong>不释放锁</strong>:<br> <code>notify()</code> 本身不会释放锁,被唤醒的线程需要等到当前线程退出 <code>synchronized</code> 块后,才能重新竞争锁并继续执行。</li>
</ul>
<table>
<thead>
<tr>
<th>方法</th>
<th>行为</th>
<th>适用场景</th>
</tr>
</thead>
<tbody><tr>
<td><code>notify()</code></td>
<td>唤醒<strong>一个</strong>等待线程</td>
<td>仅需唤醒一个线程(如单生产者-单消费者)</td>
</tr>
<tr>
<td><code>notifyAll()</code></td>
<td>唤醒<strong>所有</strong>等待线程</td>
<td>需唤醒所有线程(多线程竞争同一资源时)</td>
</tr>
</tbody></table>
<ol>
<li><strong><code>wait()</code> 必须放在 <code>while</code> 循环中</strong>,确保被唤醒后条件仍然成立。</li>
<li><strong><code>notifyAll()</code> 比 <code>notify()</code> 更安全</strong>,避免某些线程永远不被唤醒。</li>
</ol>
<h3><span id="reentrantlock">ReentrantLock()</span></h3><blockquote>
<p><code>synchronized</code>关键字用于加锁,但这种锁一是很重,二是获取时必须一直等待,没有额外的尝试机制。</p>
</blockquote>
<p><code>java.util.concurrent.locks</code>包提供的<code>ReentrantLock</code>用于替代<code>synchronized</code>加锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Counter</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> count;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> n)</span> {</span><br><span class="line"> lock.lock();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> count += n;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p><code>ReentrantLock</code>获取锁更安全;</p>
<p><code>ReentrantLock</code>可以尝试获取锁:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (lock.tryLock(<span class="number">1</span>, TimeUnit.SECONDS)) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> ...</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3><span id="condition">Condition</span></h3><p><code>Condition</code>提供的<code>await()</code>、<code>signal()</code>、<code>signalAll()</code>原理和<code>synchronized</code>锁对象的<code>wait()</code>、<code>notify()</code>、<code>notifyAll()</code>是一致的,并且其行为也是一样的:</p>
<ul>
<li><code>await()</code>会释放当前锁,进入等待状态;</li>
<li><code>signal()</code>会唤醒某个等待线程;</li>
<li><code>signalAll()</code>会唤醒所有等待线程;</li>
<li>唤醒线程从<code>await()</code>返回后需要重新获得锁。</li>
</ul>
<p>此外,和<code>tryLock()</code>类似,<code>await()</code>可以在等待指定时间后,如果还没有被其他线程通过<code>signal()</code>或<code>signalAll()</code>唤醒,可以自己醒来:</p>
<h3><span id="readwritelock">ReadWriteLock</span></h3><p>使用<code>ReadWriteLock</code>可以解决这个问题,它保证:</p>
<ul>
<li>只允许一个线程写入(其他线程既不能写入也不能读取);</li>
<li>没有写入时,多个线程允许同时读(提高性能)。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Counter</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReadWriteLock</span> <span class="variable">rwlock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantReadWriteLock</span>();</span><br><span class="line"> <span class="comment">// 注意: 一对读锁和写锁必须从同一个rwlock获取:</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Lock</span> <span class="variable">rlock</span> <span class="operator">=</span> rwlock.readLock();</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Lock</span> <span class="variable">wlock</span> <span class="operator">=</span> rwlock.writeLock();</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span>[] counts = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">10</span>];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">inc</span><span class="params">(<span class="type">int</span> index)</span> {</span><br><span class="line"> wlock.lock(); <span class="comment">// 加写锁</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> counts[index] += <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> wlock.unlock(); <span class="comment">// 释放写锁</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span>[] get() {</span><br><span class="line"> rlock.lock(); <span class="comment">// 加读锁</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> Arrays.copyOf(counts, counts.length);</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> rlock.unlock(); <span class="comment">// 释放读锁</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3><span id="stampedlock">StampedLock</span></h3><p>[[乐观锁与悲观锁]]</p>
<blockquote>
<p>乐观锁的意思就是乐观地估计读的过程中大概率不会有写入,因此被称为乐观锁。反过来,悲观锁则是读的过程中拒绝有写入,也就是写入必须等待。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Point</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">StampedLock</span> <span class="variable">stampedLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StampedLock</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</span> x;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</span> y;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">move</span><span class="params">(<span class="type">double</span> deltaX, <span class="type">double</span> deltaY)</span> {</span><br><span class="line"> <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.writeLock(); <span class="comment">// 获取写锁</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> x += deltaX;</span><br><span class="line"> y += deltaY;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> stampedLock.unlockWrite(stamp); <span class="comment">// 释放写锁</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">distanceFromOrigin</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.tryOptimisticRead(); <span class="comment">// 获得一个乐观读锁</span></span><br><span class="line"> <span class="comment">// 注意下面两行代码不是原子操作</span></span><br><span class="line"> <span class="comment">// 假设x,y = (100,200)</span></span><br><span class="line"> <span class="type">double</span> <span class="variable">currentX</span> <span class="operator">=</span> x;</span><br><span class="line"> <span class="comment">// 此处已读取到x=100,但x,y可能被写线程修改为(300,400)</span></span><br><span class="line"> <span class="type">double</span> <span class="variable">currentY</span> <span class="operator">=</span> y;</span><br><span class="line"> <span class="comment">// 此处已读取到y,如果没有写入,读取是正确的(100,200)</span></span><br><span class="line"> <span class="comment">// 如果有写入,读取是错误的(100,400)</span></span><br><span class="line"> <span class="keyword">if</span> (!stampedLock.validate(stamp)) { <span class="comment">// 检查乐观读锁后是否有其他写锁发生</span></span><br><span class="line"> stamp = stampedLock.readLock(); <span class="comment">// 获取一个悲观读锁</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> currentX = x;</span><br><span class="line"> currentY = y;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> stampedLock.unlockRead(stamp); <span class="comment">// 释放悲观读锁</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Math.sqrt(currentX * currentX + currentY * currentY);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3><span id="semphore">Semphore</span></h3><blockquote>
<p>如果要对某一受限资源进行限流访问,可以使用<code>Semaphore</code>,保证同一时间最多N个线程访问受限资源。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Point</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">StampedLock</span> <span class="variable">stampedLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StampedLock</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</span> x;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</span> y;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">move</span><span class="params">(<span class="type">double</span> deltaX, <span class="type">double</span> deltaY)</span> {</span><br><span class="line"> <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.writeLock(); <span class="comment">// 获取写锁</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> x += deltaX;</span><br><span class="line"> y += deltaY;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> stampedLock.unlockWrite(stamp); <span class="comment">// 释放写锁</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">distanceFromOrigin</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.tryOptimisticRead(); <span class="comment">// 获得一个乐观读锁</span></span><br><span class="line"> <span class="comment">// 注意下面两行代码不是原子操作</span></span><br><span class="line"> <span class="comment">// 假设x,y = (100,200)</span></span><br><span class="line"> <span class="type">double</span> <span class="variable">currentX</span> <span class="operator">=</span> x;</span><br><span class="line"> <span class="comment">// 此处已读取到x=100,但x,y可能被写线程修改为(300,400)</span></span><br><span class="line"> <span class="type">double</span> <span class="variable">currentY</span> <span class="operator">=</span> y;</span><br><span class="line"> <span class="comment">// 此处已读取到y,如果没有写入,读取是正确的(100,200)</span></span><br><span class="line"> <span class="comment">// 如果有写入,读取是错误的(100,400)</span></span><br><span class="line"> <span class="keyword">if</span> (!stampedLock.validate(stamp)) { <span class="comment">// 检查乐观读锁后是否有其他写锁发生</span></span><br><span class="line"> stamp = stampedLock.readLock(); <span class="comment">// 获取一个悲观读锁</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> currentX = x;</span><br><span class="line"> currentY = y;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> stampedLock.unlockRead(stamp); <span class="comment">// 释放悲观读锁</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Math.sqrt(currentX * currentX + currentY * currentY);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3><span id="javautilconcurrent包提供的线程安全集合">java.util.concurrent包提供的线程安全集合</span></h3><p>针对<code>List</code>、<code>Map</code>、<code>Set</code>、<code>Deque</code>等,<code>java.util.concurrent</code>包也提供了对应的并发集合类。我们归纳一下:</p>
<table>
<thead>
<tr>
<th>interface</th>
<th>non-thread-safe</th>
<th>thread-safe</th>
</tr>
</thead>
<tbody><tr>
<td>List</td>
<td>ArrayList</td>
<td>CopyOnWriteArrayList</td>
</tr>
<tr>
<td>Map</td>
<td>HashMap</td>
<td>ConcurrentHashMap</td>
</tr>
<tr>
<td>Set</td>
<td>HashSet / TreeSet</td>
<td>CopyOnWriteArraySet</td>
</tr>
<tr>
<td>Queue</td>
<td>ArrayDeque / LinkedList</td>
<td>ArrayBlockingQueue / LinkedBlockingQueue</td>
</tr>
<tr>
<td>Deque</td>
<td>ArrayDeque / LinkedList</td>
<td>LinkedBlockingDeque</td>
</tr>
</tbody></table>
<p>还提供了新旧集合转换器</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Map</span> <span class="variable">unsafeMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line"><span class="type">Map</span> <span class="variable">threadSafeMap</span> <span class="operator">=</span> Collections.synchronizedMap(unsafeMap);</span><br></pre></td></tr></table></figure>
<h3><span id="javautilconcurrentatomic包提供原子操作">java.util.concurrent.atomic包提供原子操作</span></h3><p>我们以<code>AtomicInteger</code>为例,它提供的主要操作有:</p>
<ul>
<li>增加值并返回新值:<code>int addAndGet(int delta)</code></li>
<li>加1后返回新值:<code>int incrementAndGet()</code></li>
<li>获取当前值:<code>int get()</code></li>
<li>用CAS方式设置:<code>int compareAndSet(int expect, int update)</code></li>
</ul>
<p>Atomic类是通过无锁(lock-free)的方式实现的线程安全(thread-safe)访问。它的主要原理是利用了CAS:Compare and Set。</p>
<p>[[CAS]]是指,在这个操作中,如果<code>AtomicInteger</code>的当前值是<code>prev</code>,那么就更新为<code>next</code>,返回<code>true</code>。如果<code>AtomicInteger</code>的当前值不是<code>prev</code>,就什么也不干,返回<code>false</code>。通过CAS操作并配合<code>do ... while</code>循环,即使其他线程修改了<code>AtomicInteger</code>的值,最终的结果也是正确的。</p>
<h2><span id="使用线程池">使用线程池</span></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建固定大小的线程池:</span></span><br><span class="line"><span class="type">ExecutorService</span> <span class="variable">executor</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line"><span class="comment">// 提交任务:</span></span><br><span class="line">executor.submit(task1);</span><br><span class="line">executor.submit(task2);</span><br><span class="line">executor.submit(task3);</span><br><span class="line">executor.submit(task4);</span><br><span class="line">executor.submit(task5);</span><br><span class="line"><span class="comment">// 关闭线程池;</span></span><br><span class="line">es.shutdown();</span><br></pre></td></tr></table></figure>
<p>因为<code>ExecutorService</code>只是接口,Java标准库提供的几个常用实现类有:</p>
<ul>
<li>FixedThreadPool:线程数固定的线程池;</li>
<li>CachedThreadPool:线程数根据任务动态调整的线程池;</li>
<li>SingleThreadExecutor:仅单线程执行的线程池。</li>
<li>ScheduledThreadPool:(任务定期触发)</li>
</ul>
<h2><span id="future">Future</span></h2><p><code>Runnable</code>接口有个问题,它的方法没有返回值。如果任务需要一个返回结果,那么只能保存到变量。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Task</span> <span class="keyword">implements</span> <span class="title class_">Callable</span><String> {</span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line"> <span class="keyword">return</span> longTimeCalculation(); </span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>获取结果:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ExecutorService</span> <span class="variable">executor</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">4</span>); </span><br><span class="line"><span class="comment">// 定义任务:</span></span><br><span class="line">Callable<String> task = <span class="keyword">new</span> <span class="title class_">Task</span>();</span><br><span class="line"><span class="comment">// 提交任务并获得Future:</span></span><br><span class="line">Future<String> future = executor.submit(task);</span><br><span class="line"><span class="comment">// 从Future获取异步执行返回的结果:</span></span><br><span class="line"><span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> future.get(); <span class="comment">// 可能阻塞</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>当我们提交一个<code>Callable</code>任务后,我们会同时获得一个<code>Future</code>对象,然后,我们在主线程某个时刻调用<code>Future</code>对象的<code>get()</code>方法,就可以获得异步执行的结果。在调用<code>get()</code>时,如果异步任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么<code>get()</code>会阻塞,直到任务完成后才返回结果。</p>
<p>一个<code>Future<V></code>接口表示一个未来可能会返回的结果,它定义的方法有:</p>
<ul>
<li><code>get()</code>:获取结果(可能会等待)</li>
<li><code>get(long timeout, TimeUnit unit)</code>:获取结果,但只等待指定的时间;</li>
<li><code>cancel(boolean mayInterruptIfRunning)</code>:取消当前任务;</li>
<li><code>isDone()</code>:判断任务是否已完成。</li>
</ul>
<h2><span id="completablefuture">CompletableFuture</span></h2><p>从Java 8开始引入了<code>CompletableFuture</code>,它针对<code>Future</code>做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。</p>
<p><code>Future</code> 和 <code>CompletableFuture</code> 都支持异步计算,但 <code>CompletableFuture</code> 提供了更灵活、更强大的 <strong>链式调用(串行执行)</strong> 能力,而 <code>Future</code> 在这方面的能力非常有限</p>
<ul>
<li><strong><code>Future</code> 无法直接依赖另一个 <code>Future</code> 的结果</strong>,必须手动等待前一个 <code>Future</code> 完成,再启动下一个任务。</li>
<li><strong>代码复杂</strong>:需要嵌套 <code>get()</code> 调用,容易导致阻塞或回调地狱。</li>
</ul>
<p><code>CompletableFuture</code> 提供了 <code>thenApply()</code>, <code>thenCompose()</code>, <code>thenAccept()</code> 等方法,可以 <strong>直接在前一个任务完成后触发下一个任务</strong>,无需手动等待。</p>
<h2><span id="forkjoin">ForkJoin</span></h2><p>Fork/Join是一种基于“<strong>分治</strong>”的算法:通过分解任务,并行执行,最后合并结果得到最终结果。</p>
<p><code>ForkJoinPool</code>线程池可以把一个大任务分拆成小任务并行执行,任务类必须继承自<code>RecursiveTask</code>或<code>RecursiveAction</code>。</p>
<p>使用Fork/Join模式可以进行并行计算以提高效率。</p>
<h2><span id="threadlocal">ThreadLocal</span></h2><p><code>Thread.currentThread()</code>获取当前线程。</p>
<p>如何在一个线程中传递状态?</p>
<blockquote>
<p>这种在一个线程中,横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等。<br>给每个方法增加一个context参数非常麻烦,而且有些时候,如果调用链有无法修改源码的第三方库,<code>User</code>对象就传不进去了。</p>
</blockquote>
<p>Java标准库提供了一个特殊的<code>ThreadLocal</code>,它可以在一个线程中传递同一个对象。</p>
<p><code>ThreadLocal</code>表示线程的“局部变量”,它确保每个线程的<code>ThreadLocal</code>变量都是各自独立的;</p>
<p><code>ThreadLocal</code>适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递);</p>
<blockquote>
<p>使用<code>ThreadLocal</code>要用<code>try ... finally</code>结构,并在<code>finally</code>中清除。why?</p>
</blockquote>
<p><strong>ThreadLocal 使用注意事项</strong></p>
<ol>
<li><p><strong>存储机制</strong></p>
<ul>
<li><code>ThreadLocal.set()</code> 将数据存储到<strong>当前线程的 <code>ThreadLocalMap</code></strong>中,而非 <code>ThreadLocal</code> 本身。</li>
<li>线程死亡时,<code>ThreadLocalMap</code> 自动销毁,无需手动 <code>remove()</code>。</li>
</ul>
</li>
<li><p><strong>线程池场景</strong></p>
<ul>
<li><strong>线程会被复用</strong>(如 Tomcat、Spring 线程池),导致 <code>ThreadLocal</code> 数据残留,引发内存泄漏或数据错乱。</li>
<li><strong>必须清理</strong>:在 <code>finally</code> 中调用 <code>ThreadLocal.remove()</code>,或使用 <code>AutoCloseable</code> 自动清除。</li>
</ul>
</li>
<li><p><strong>最佳实践</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">try {</span><br><span class="line"> threadLocal.set(value);</span><br><span class="line"> // 业务逻辑...</span><br><span class="line">} finally {</span><br><span class="line"> threadLocal.remove(); // 强制清除,避免线程池复用污染</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p> <strong>或</strong>(推荐)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">try (AutoCloseable cleanup = threadLocal.withInitial(value)) {</span><br><span class="line"> // 业务逻辑...</span><br><span class="line">} // 自动调用 remove()</span><br></pre></td></tr></table></figure></li>
</ol>
<p><strong>核心原则</strong>:只要用线程池,就必须清理 <code>ThreadLocal</code>!</p>
<h2><span id="虚拟线程">虚拟线程</span></h2><p>Java 19引入的虚拟线程是为了解决<strong>IO密集型任</strong>务的吞吐量,它可以高效通过少数线程去调度大量虚拟线程;</p>
<p>虚拟线程在执行到IO操作或Blocking操作时,会<strong>自动切换到其他虚拟线程执行</strong>,从而避免当前线程等待,能最大化线程的执行效率;</p>
<p>虚拟线程使用普通线程相同的接口,最大的好处是无需修改任何代码,就可以将现有的IO操作异步化获得更大的吞吐能力。</p>
<p>计算密集型任务不应使用虚拟线程,只能通过增加CPU核心解决,或者利用分布式计算资源。</p>
</div></article></div></div><div class="column column-left is-4-tablet is-4-desktop is-4-widescreen order-1"><div class="card widget" data-type="profile"><div class="card-content"><nav class="level"><div class="level-item has-text-centered flex-shrink-1"><div><figure class="image is-128x128 mx-auto mb-2"><img class="avatar" src="https://www.gravatar.com/avatar/08b2a2060689e7a95e17fa215d2bc4a9?s=128" alt="w1m024"></figure><p class="title is-size-4 is-block" style="line-height:inherit;">w1m024</p><p class="is-size-6 is-flex justify-content-center"><i class="fas fa-map-marker-alt mr-1"></i><span>Beijing, China</span></p></div></div></nav><nav class="level is-mobile"><div class="level-item has-text-centered is-marginless"><div><p class="heading">文章</p><a href="/archives/"><p class="title">3</p></a></div></div><div class="level-item has-text-centered is-marginless"><div><p class="heading">分类</p><a href="/categories/"><p class="title">0</p></a></div></div><div class="level-item has-text-centered is-marginless"><div><p class="heading">标签</p><a href="/tags/"><p class="title">2</p></a></div></div></nav><div class="level"><a class="level-item button is-primary is-rounded" href="https://github.com/ppoffice" target="_blank" rel="me noopener">关注我</a></div></div></div><!--!--><div class="card widget" data-type="links"><div class="card-content"><div class="menu"><h3 class="menu-label">链接</h3><ul class="menu-list"><li><a class="level is-mobile" href="https://github.com/w1m024" target="_blank" rel="noopener"><span class="level-left"><span class="level-item">GitHub</span></span><span class="level-right"><span class="level-item tag">github.com</span></span></a></li><li><a class="level is-mobile" href="https://gitee.com/w1mm" target="_blank" rel="noopener"><span class="level-left"><span class="level-item">Gitee</span></span><span class="level-right"><span class="level-item tag">gitee.com</span></span></a></li><li><a class="level is-mobile" href="https://leetcode.cn/u/w1m024/" target="_blank" rel="noopener"><span class="level-left"><span class="level-item">LeetCode</span></span><span class="level-right"><span class="level-item tag">leetcode.cn</span></span></a></li></ul></div></div></div><!--!--><div class="card widget" data-type="recent-posts"><div class="card-content"><h3 class="menu-label">最新文章</h3><article class="media"><div class="media-content"><p class="date"><time dateTime="2025-06-20T08:08:54.000Z">2025-06-20</time></p><p class="title"><a href="/2025/06/20/%E9%81%93%E6%99%AE%E4%BA%91%E9%9D%A2%E8%AF%95/">道普云面试</a></p></div></article><article class="media"><div class="media-content"><p class="date"><time dateTime="2025-06-08T08:51:36.000Z">2025-06-08</time></p><p class="title"><a href="/2025/06/08/%E6%9B%B2%E9%80%9F%E7%A7%91%E6%8A%80%E7%AC%94%E8%AF%95/">曲速科技笔试</a></p></div></article><article class="media"><div class="media-content"><p class="date"><time dateTime="2025-06-08T07:11:55.000Z">2025-06-08</time></p><p class="title"><a href="/2025/06/08/java%E5%A4%9A%E7%BA%BF%E7%A8%8B/">java多线程笔记</a></p></div></article></div></div><div class="card widget" data-type="archives"><div class="card-content"><div class="menu"><h3 class="menu-label">归档</h3><ul class="menu-list"><li><a class="level is-mobile" href="/archives/2025/06/"><span class="level-start"><span class="level-item">六月 2025</span></span><span class="level-end"><span class="level-item tag">3</span></span></a></li></ul></div></div></div><div class="card widget" data-type="tags"><div class="card-content"><div class="menu"><h3 class="menu-label">标签</h3><div class="field is-grouped is-grouped-multiline"><div class="control"><a class="tags has-addons" href="/tags/java/"><span class="tag">java</span><span class="tag">1</span></a></div><div class="control"><a class="tags has-addons" href="/tags/%E9%9D%A2%E8%AF%95/"><span class="tag">面试</span><span class="tag">1</span></a></div></div></div></div></div></div><!--!--></div></div></section><footer class="footer"><div class="container"><div class="level"><div class="level-start"><a class="footer-logo is-block mb-2" href="/"><img src="/img/logo.svg" alt="w1m's Blog" height="28"></a><p class="is-size-7"><span>© 2025 w1m024</span> Powered by <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a> & <a href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank" rel="noopener">Icarus</a></p><p class="is-size-7">© 2019</p></div><div class="level-end"><div class="field has-addons"><p class="control"><a class="button is-transparent is-large" target="_blank" rel="noopener" title="Creative Commons" href="https://creativecommons.org/"><i class="fab fa-creative-commons"></i></a></p><p class="control"><a class="button is-transparent is-large" target="_blank" rel="noopener" title="Attribution 4.0 International" href="https://creativecommons.org/licenses/by/4.0/"><i class="fab fa-creative-commons-by"></i></a></p><p class="control"><a class="button is-transparent is-large" target="_blank" rel="noopener" title="Download on GitHub" href="https://github.com/ppoffice/hexo-theme-icarus"><i class="fab fa-github"></i></a></p></div></div></div></div></footer><script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/npm/moment@2.22.2/min/moment-with-locales.min.js"></script><script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.4/dist/clipboard.min.js" defer></script><script>moment.locale("zh-cn");</script><script>var IcarusThemeSettings = {
article: {
highlight: {
clipboard: true,
fold: 'unfolded'
}
}
};</script><script data-pjax src="/js/column.js"></script><script src="/js/animation.js"></script><a id="back-to-top" title="回到顶端" href="javascript:;"><i class="fas fa-chevron-up"></i></a><script data-pjax src="/js/back_to_top.js" defer></script><!--!--><!--!--><!--!--><script src="https://cdn.jsdelivr.net/npm/cookieconsent@3.1.1/build/cookieconsent.min.js" defer></script><script>window.addEventListener("load", () => {
window.cookieconsent.initialise({
type: "info",
theme: "edgeless",
static: false,
position: "bottom-left",
content: {
message: "此网站使用Cookie来改善您的体验。",
dismiss: "知道了!",
allow: "允许使用Cookie",
deny: "拒绝",
link: "了解更多",
policy: "Cookie政策",
href: "https://www.cookiesandyou.com/",
},
palette: {
popup: {
background: "#edeff5",
text: "#838391"
},
button: {
background: "#4b81e8"
},
},
});
});</script><script src="https://cdn.jsdelivr.net/npm/lightgallery@1.10.0/dist/js/lightgallery.min.js" defer></script><script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.8.1/dist/js/jquery.justifiedGallery.min.js" defer></script><script>window.addEventListener("load", () => {
if (typeof $.fn.lightGallery === 'function') {
$('.article').lightGallery({ selector: '.gallery-item' });
}
if (typeof $.fn.justifiedGallery === 'function') {
if ($('.justified-gallery > p > .gallery-item').length) {
$('.justified-gallery > p > .gallery-item').unwrap();
}
$('.justified-gallery').justifiedGallery();
}
});</script><!--!--><!--!--><script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script><script src="/js/pjax.js"></script><!--!--><!--!--><!--!--><script data-pjax src="/js/main.js" defer></script><div class="searchbox"><div class="searchbox-container"><div class="searchbox-header"><div class="searchbox-input-container"><input class="searchbox-input" type="text" placeholder="想要查找什么..."></div><a class="searchbox-close" href="javascript:;">×</a></div><div class="searchbox-body"></div></div></div><script data-pjax src="/js/insight.js" defer></script><script data-pjax>document.addEventListener('DOMContentLoaded', function () {
loadInsight({"contentUrl":"/content.json"}, {"hint":"想要查找什么...","untitled":"(无标题)","posts":"文章","pages":"页面","categories":"分类","tags":"标签"});
});</script></body></html>