-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlocal-search.xml
More file actions
442 lines (212 loc) · 154 KB
/
local-search.xml
File metadata and controls
442 lines (212 loc) · 154 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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>rabbitmq install</title>
<link href="/2021/10/08/rabbitmq%20install/"/>
<url>/2021/10/08/rabbitmq%20install/</url>
<content type="html"><![CDATA[<p><strong>RabbitMQ</strong>是实现了高级消息队列协议(AMQP)的<strong>开源</strong>消息代理软件.</p><p>是用<strong>Erlang</strong>语言编写的.</p><p>拥有着微秒级的响应速度.</p><p>不支持分布式, 但是支持集群的形式部署.</p><h2 id="1-安装erlang"><a href="#1-安装erlang" class="headerlink" title="1.安装erlang"></a>1.安装erlang</h2><p>具体版本可以在erlang官网上查看 <a href="https://www.erlang-solutions.com/downloads/">https://www.erlang-solutions.com/downloads/</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">sudo yum -y update<br>sudo yum -y install epel-release<br><span class="hljs-meta">#</span><span class="bash"> 下载 && 安装 -> erlang</span><br>cd ~ && wget https://packages.erlang-solutions.com/erlang/rpm/centos/7/x86_64/esl-erlang_24.0.2-1~centos~7_amd64.rpm<br>sudo yum -y install esl-erlang_24.0.2-1~centos~7_amd64.rpm<br><span class="hljs-meta">#</span><span class="bash"> 验证是否安装成功</span><br>erl<br></code></pre></td></tr></table></figure><h2 id="2-安装RabbitMQ"><a href="#2-安装RabbitMQ" class="headerlink" title="2.安装RabbitMQ"></a>2.安装RabbitMQ</h2><p>具体版本可以在github上查看 <a href="https://github.com/rabbitmq/rabbitmq-server/releases">https://github.com/rabbitmq/rabbitmq-server/releases</a></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs sh"><span class="hljs-comment"># 下载 && 安装 -> rabbitmq</span><br>wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.9.7/rabbitmq-server-3.9.7-1.el7.noarch.rpm<br>sudo yum install -y rabbitmq-server-3.9.7-1.el7.noarch.rpm<br><span class="hljs-comment"># 启动 && 设置开机自启 -> rabbit服务</span><br>systemctl start rabbitmq-server.service<br>systemctl <span class="hljs-built_in">enable</span> rabbitmq-server.service<br><span class="hljs-comment"># 设置对应防火墙端口 [4369,25672,5671,5672,15672,61613,61614,1883,8883]</span><br>sudo firewall-cmd --zone=public --permanent --add-port=4369/tcp --add-port=25672/tcp --add-port=5671-5672/tcp --add-port=15672/tcp --add-port=61613-61614/tcp --add-port=1883/tcp --add-port=8883/tcp<br>sudo firewall-cmd --reload<br></code></pre></td></tr></table></figure><h3 id="3-开启RabbitMQ管理插件"><a href="#3-开启RabbitMQ管理插件" class="headerlink" title="3.开启RabbitMQ管理插件"></a>3.开启RabbitMQ管理插件</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta">#</span><span class="bash"> 开启插件, 会默认创建一个用户为guest(管理员), 一般情况选择删除该用户重新创建</span><br>sudo rabbitmq-plugins enable rabbitmq_management<br><span class="hljs-meta">#</span><span class="bash"> 删除默认用户</span><br>sudo rabbitmqctl delete_user guest<br><span class="hljs-meta">#</span><span class="bash"> 添加用户</span><br>sudo rabbitmqctl add_user root<br><span class="hljs-meta">#</span><span class="bash"> 设置角色</span><br>sudo rabbitmqctl set_user_tags root administrator<br><span class="hljs-meta">#</span><span class="bash"> 设置权限</span><br>sudo rabbitmqctl set_permissions -p / root ".*" ".*" ".*"<br></code></pre></td></tr></table></figure><p>浏览器访问{IP}:15672即可</p><blockquote><p>CentOS 7 安装rabbitmq脚本 <a href="https://github.com/liCells/linux_shell/blob/main/install_rabbitmq.sh">https://github.com/liCells/linux_shell/blob/main/install_rabbitmq.sh</a></p></blockquote>]]></content>
<categories>
<category>中间件</category>
</categories>
<tags>
<tag>mq</tag>
</tags>
</entry>
<entry>
<title>命名</title>
<link href="/2021/08/27/%E5%91%BD%E5%90%8D/"/>
<url>/2021/08/27/%E5%91%BD%E5%90%8D/</url>
<content type="html"><![CDATA[<h2 id="统一资源管理类命名"><a href="#统一资源管理类命名" class="headerlink" title="统一资源管理类命名"></a>统一资源管理类命名</h2><h4 id="Bootstrap-Starter"><a href="#Bootstrap-Starter" class="headerlink" title="Bootstrap, Starter"></a>Bootstrap, Starter</h4><p> 一般来说作为程序启动器使用, 或者是某个项目的基类</p><h4 id="Processor"><a href="#Processor" class="headerlink" title="Processor"></a>Processor</h4><p> 某一类功能的处理器, 用来表示某个处理过程</p><h4 id="Manager"><a href="#Manager" class="headerlink" title="Manager"></a>Manager</h4><p> 对有生命状态的对象进行管理, 通常作为某一类资源的管理入口</p><h4 id="Holder"><a href="#Holder" class="headerlink" title="Holder"></a>Holder</h4><p> 表示持有某个或者某类对象的引用,并可以对其进行统一管理</p><h4 id="Factory"><a href="#Factory" class="headerlink" title="Factory"></a>Factory</h4><p> 工厂模式的命名</p><h4 id="Provider"><a href="#Provider" class="headerlink" title="Provider"></a>Provider</h4><p> 一般是接口或者抽象类, 以便能够完成子实现</p><h4 id="Registrar"><a href="#Registrar" class="headerlink" title="Registrar"></a>Registrar</h4><p> 注册并管理一系列资源</p><h4 id="Engine"><a href="#Engine" class="headerlink" title="Engine"></a>Engine</h4><p> 一般都是核心模块, 用来处理某一类功能</p><h4 id="Service"><a href="#Service" class="headerlink" title="Service"></a>Service</h4><h4 id="Task"><a href="#Task" class="headerlink" title="Task"></a>Task</h4><h2 id="传播类命名"><a href="#传播类命名" class="headerlink" title="传播类命名"></a>传播类命名</h2><p> 有些参数需要在各种方法中传递, 就需要一个单独的类来统一处理, 最终传递的就是统一的一个类对象</p><h4 id="Context"><a href="#Context" class="headerlink" title="Context"></a>Context</h4><h4 id="Propagator"><a href="#Propagator" class="headerlink" title="Propagator"></a>Propagator</h4><h2 id="回调类命名"><a href="#回调类命名" class="headerlink" title="回调类命名"></a>回调类命名</h2><h4 id="Handler-Callback-Trigger-Listener"><a href="#Handler-Callback-Trigger-Listener" class="headerlink" title="Handler, Callback, Trigger, Listener"></a>Handler, Callback, Trigger, Listener</h4><ul><li>Handler一般都是真正要处理逻辑的对象</li><li>Callback一般都是一个接口, 用于处理某一类的消息</li><li>Trigger一般都是某类事件的处理</li><li>Listener一般用在观察者模式中</li></ul><h4 id="Aware"><a href="#Aware" class="headerlink" title="Aware"></a>Aware</h4><h2 id="监控类命名"><a href="#监控类命名" class="headerlink" title="监控类命名"></a>监控类命名</h2><h4 id="Accumulator"><a href="#Accumulator" class="headerlink" title="Accumulator"></a>Accumulator</h4><p> 累加器</p><h4 id="Metric-Monitor"><a href="#Metric-Monitor" class="headerlink" title="Metric, Monitor"></a>Metric, Monitor</h4><p> 一般表示监控</p><h4 id="Estimator"><a href="#Estimator" class="headerlink" title="Estimator"></a>Estimator</h4><p> 统计类, 一般指某一类的统计计算</p><h4 id="Tracker"><a href="#Tracker" class="headerlink" title="Tracker"></a>Tracker</h4><p> 跟踪器</p><h2 id="内存管理类命名"><a href="#内存管理类命名" class="headerlink" title="内存管理类命名"></a>内存管理类命名</h2><h4 id="Allocator"><a href="#Allocator" class="headerlink" title="Allocator"></a>Allocator</h4><p> 内存分配器</p><h4 id="Arena"><a href="#Arena" class="headerlink" title="Arena"></a>Arena</h4><p> 资源的管理</p><h4 id="Chunk"><a href="#Chunk" class="headerlink" title="Chunk"></a>Chunk</h4><h4 id="Pool"><a href="#Pool" class="headerlink" title="Pool"></a>Pool</h4><p> 池化类</p><h2 id="过滤处理类命名"><a href="#过滤处理类命名" class="headerlink" title="过滤处理类命名"></a>过滤处理类命名</h2><h4 id="Detector"><a href="#Detector" class="headerlink" title="Detector"></a>Detector</h4><p> 探测器, 一般处理一些事件, 在事件发生时进行捕获或者说响应</p><h4 id="Evaluator"><a href="#Evaluator" class="headerlink" title="Evaluator"></a>Evaluator</h4><p> 一般用于处理计算类</p><h4 id="Interceptor"><a href="#Interceptor" class="headerlink" title="Interceptor"></a>Interceptor</h4><p> 拦截器</p><h4 id="Filter"><a href="#Filter" class="headerlink" title="Filter"></a>Filter</h4><p> 过滤器</p><h4 id="Pipeline-Chain"><a href="#Pipeline-Chain" class="headerlink" title="Pipeline, Chain"></a>Pipeline, Chain</h4><p> 一般在责任链模式中使用</p><h2 id="结构类命名"><a href="#结构类命名" class="headerlink" title="结构类命名"></a>结构类命名</h2><h4 id="Cache"><a href="#Cache" class="headerlink" title="Cache"></a>Cache</h4><p> 缓存</p><h4 id="Buffer"><a href="#Buffer" class="headerlink" title="Buffer"></a>Buffer</h4><p> 缓冲</p><h4 id="Wrapper"><a href="#Wrapper" class="headerlink" title="Wrapper"></a>Wrapper</h4><p> 包装, 用于包装某个类做附加处理</p><h4 id="Iterator"><a href="#Iterator" class="headerlink" title="Iterator"></a>Iterator</h4><p> 迭代器</p><h4 id="Batch"><a href="#Batch" class="headerlink" title="Batch"></a>Batch</h4><p> 批处理</p><h4 id="Limiter"><a href="#Limiter" class="headerlink" title="Limiter"></a>Limiter</h4><p> 限流器</p>]]></content>
<categories>
<category>规范</category>
</categories>
<tags>
<tag>规范</tag>
</tags>
</entry>
<entry>
<title>nginx 配置</title>
<link href="/2021/08/11/nginx%20%E9%85%8D%E7%BD%AE/"/>
<url>/2021/08/11/nginx%20%E9%85%8D%E7%BD%AE/</url>
<content type="html"><![CDATA[<p>nginx是一个高性能的HTTP和反向代理服务器, 邮件代理服务器, TCP/UDP 代理服务器</p><ul><li>一个主进程和多个工作进程; 工作进程在非root用户下运行;</li><li>可以在不中断服务的情况下更新配置</li><li>支持epoll, kqueue等</li><li>提供静态和索引文件</li><li>负载均衡和容错</li><li>使用缓存加速反向代理</li><li>基于客户端地址的访问控制</li><li>限制来自一个地址的同时连接数</li><li>TCP和UDP的通用代理</li></ul><h3 id="nginx-conf的配置项记录"><a href="#nginx-conf的配置项记录" class="headerlink" title="nginx.conf的配置项记录"></a>nginx.conf的配置项记录</h3><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><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></pre></td><td class="code"><pre><code class="hljs apl"># 全局块<br><br># 指定可以运行服务的用户和用户组, 去掉或者写为nonody的话代表所有用户都可以运行<br>user nginx;<br># 工作线程数(worker进程), 可以指定数量, 也可以使用auto自动处理<br>worker_processes auto;<br># 指定pid文件的存放路径, 主进程的进程ID会写入文件<br>pid nginx.pid<br><br>worker_rlimit_nofile 65535;<br><br># events块<br>events {<br># 连接处理方式, 一般情况nginx会自动选择最好的方法, 但是也可以自行配置<br># select | poll | kqueue | epoll | /dev/poll | eventport<br> use epoll;<br> # 设置工作进程可以打开的最大同时连接数, 注意这个连接数不止代表了客户端连接的数量, 还有代理服务器的连接<br> worker_connections 1024;<br>}<br><br># hhtp块<br>http {<br># 开启或禁用在错误界面和服务器响应时返回的nginx版本<br> server_tokens off;<br> # 映射表<br> include mime.types;<br> # 响应类型<br> default_type application/octet-stream;<br> # 默认情况下数据发送时, 不会直接发送, 而是会等待较多的数据组成数据包进行发送, 可以提高IO效率, 但是每次发送数据很少的话等待时间会长<br> tcp_nodelay on;<br> # 开启gzip压缩功能, 提高页面响应, 也可以节省网络带宽<br> gzip on;<br> # 允许压缩的最小字节<br> gzip_min_length 1024k;<br> # 区分http协议版本, 有些浏览器可能不支持gzip解压<br> gzip_http_version 1.1;<br> # 压缩等级, 越小压缩越快, 相对也更占用CPU<br> gzip_comp_level 2;<br> # gzip内存大小, 该设置代表以32k为单位的4倍大小申请内存空间<br> gzip_buffers 4 32k;<br> # 设置需要压缩的类型<br> gzip_types text/plain text/css application/xml;<br> <br> # 向客户端发送的数据大于设置值时进行发送, 提高IO效率<br> postpone_output 1024;<br> # 设置读取客户端请求内容的超时时间<br> client_body_timeout 1m;<br> # 设置发送给客户端内容的超时时间<br> send_timeout 1m;<br> # 前者代表对服务端连接的持续时间, 后者代表给用户端的Keep-Alive超时时间<br> keepalive_timeout 65 [header_timeout];<br><br> server {<br> listen 80;<br> server_name localhost;<br><br> location /folder/ {<br> # 是否使用sendfile传输文件, 可以在http | server | location中配置<br> sendfile on;<br> # 限制sendfile一次发送的大小, 可以在http | server | location中配置<br> sendfile_max_chunk 256k;<br> # (启用sendfile才会生效) 是否使用socke的TCP_CORK, 开启后, 数据包会在有一定大小之后再发送<br> tcp_nopush on;<br> tcp_nodelay on;<br> # 0.8.12版本开始支持, 启用aio时, 会自动启动directio, 如果文件大于设定值, 那么会走aio传输文件, 如果小于则会走sendfile<br> aio threads;<br> directio 512k;<br> # 从磁盘读取的响应区大小, 该设置代表以128K为单位的1倍大小申请内存空间<br> output_buffers 1 128k;<br> # 设置客户端请求大小的限制, 如果超出返回413错误, 注意浏览器可能无法正确显示该错误<br> client_max_body_size 100m;<br> # 用于访问文件, 例: /folder/test.jpg -> /data/pic/test.jpg<br> alias /data/pic/;<br> # 用于访问文件, 例: /folder/test.jpg -> /data/pic/folder/test.jpg<br> root /data/pic/;<br> # 反向代理, 例: /folder/test.jpg -> http://127.0.0.1:8080/test.jpg<br> proxy_pass http://127.0.0.1:8080/<br> index index.html index.htm;<br><br> # 开启索引功能<br> autoindex on;<br> # 关闭计算文件确切大小 (bytes), 显示大概大小 (kb, mb, gb)<br> autoindex_exact_size off;<br> # 显示本机时间而不是显示GMT时间<br> autoindex_localtime on;<br> # 配置编码<br> charset utf-8;<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>中间件</category>
<category>nginx</category>
</categories>
<tags>
<tag>中间件</tag>
<tag>http服务器</tag>
<tag>nginx</tag>
</tags>
</entry>
<entry>
<title>JMM</title>
<link href="/2021/07/14/JMM/"/>
<url>/2021/07/14/JMM/</url>
<content type="html"><![CDATA[<h3 id="Java-内存模型-Java-Memory-Model-JMM"><a href="#Java-内存模型-Java-Memory-Model-JMM" class="headerlink" title="Java 内存模型 (Java Memory Model, JMM)"></a>Java 内存模型 (Java Memory Model, JMM)</h3><p>在Java虚拟机中定义的规范, 用于屏蔽各种硬件和操作系统的内存访问差异.</p><p><img src="D:\koncks_project\github\notes-to-build\source\imgs\JMM_1.png" alt="image-20211009144318894"></p><p>JVM内部使用的Java 内存模型在线程堆栈和堆之间划分内存</p><p>Java虚拟机中运行的每个线程都有自己的<strong>线程堆栈</strong>. <strong>线程堆栈</strong>包含着有关线程调用哪些方法以到达当前执行点的信息.</p><p><strong>线程堆栈</strong>中包含了每个正在执行的方法中的局部变量, 一个线程只能访问他自己的<strong>线程堆栈</strong>, 也就是说, 局部变量对于其他线程是不可见的, 即使都是一模一样的变量, 但也是在自己的<strong>线程堆栈</strong>中创建自己的局部变量.</p><blockquote><p>所有的基本类型, 都会存储在线程堆栈中, 因此对于其他的线程来说是不可见的. 一个线程可以将原始变量的副本传递给其他的线程, 但是无法共享原始变量本身.</p></blockquote><p><strong>堆</strong>中包含了所有创建的对象, 这里是不区分线程的, 这个对象也包括基本类型的包装类Integer这些.</p><p><img src="D:\koncks_project\github\notes-to-build\source\imgs\JMM_2.png" alt="image-20211009144934410"></p><p>如果<strong>局部变量</strong>是<strong>基本类型</strong>, 那么这个变量完全存储在<strong>线程堆栈</strong>中, 但是局部变量也可以是对象的引用, 在这种情况下, 引用会存储在线程堆栈中, 但是对象本身存储在堆中, 这个引用就是对应的局部变量.</p><blockquote><p>这就是为什么对象引用作为方法参数传递时修改对象值可以获取到对应的值, 而基本类型时无法获取到, 因为局部变量只是对象的引用, 但是对象本身都是堆中的一个, 所以改变后可以获取到, 而基本类型因为只是在方法中的局部变量, 每次都是一个单独的变量, 自然也就无法影响到父方法中的变量.</p></blockquote>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>JMM</tag>
</tags>
</entry>
<entry>
<title>Redis 雪崩 穿透 击穿</title>
<link href="/2021/07/12/Redis%20%E9%9B%AA%E5%B4%A9%20%E7%A9%BF%E9%80%8F%20%E5%87%BB%E7%A9%BF/"/>
<url>/2021/07/12/Redis%20%E9%9B%AA%E5%B4%A9%20%E7%A9%BF%E9%80%8F%20%E5%87%BB%E7%A9%BF/</url>
<content type="html"><![CDATA[<h2 id="雪崩-Cache-Avalanche"><a href="#雪崩-Cache-Avalanche" class="headerlink" title="雪崩 Cache Avalanche"></a>雪崩 Cache Avalanche</h2><blockquote><p>假如现在数据量请求较大, 有大部分会击中缓存, 最终落到DB的请求较小, 但是缓存中突然大量的key同时失效, 这个时候大量请求直接打到DB, 导致DB在DBA处理警告之前便已经宕机.</p></blockquote><blockquote><p>缓存出现宕机情况, 导致请求直接打到DB</p></blockquote><ol><li>解决方案一般是加随机值处理, 但是有些热点数据可以考虑不设置过期时间, 通过异步线程更新数据, 或定义逻辑过期时间, 过了该时间就进行更新.</li><li>将缓存层设计为高可用, 即使部分节点宕掉, 依旧可以继续提供服务.</li><li>可以做熔断限流处理, 保证底层系统可以运行但避开高负载的情况.</li></ol><h2 id="穿透-Cache-Penetration"><a href="#穿透-Cache-Penetration" class="headerlink" title="穿透 Cache Penetration"></a>穿透 Cache Penetration</h2><blockquote><p>对于第一次数据访问, 缓存中并没有数据, 所有的并发请求都会打入DB</p></blockquote><blockquote><p>对于数据库中没有的数据进行访问</p></blockquote><blockquote><p>数据库中本身就是空的, 导致缓存也为空</p></blockquote><ol><li>在服务启动时, 便将热点数据加载到缓存中</li><li>对于缓存的key, 进行规范的命名, 在检查缓存的位置校验一次key的命名.</li><li>检查缓存时如果没有找到, 那么再检查一次是否已有线程去DB中获取, 如果有就等待, 没有就把key加载到缓存中, 注意这一块需要同步处理, 到数据库中查询无论有没有数据都加载到缓存中, 但是时间可以相对较短.</li></ol><h2 id="击穿-Hotspot-Invalid"><a href="#击穿-Hotspot-Invalid" class="headerlink" title="击穿 Hotspot Invalid"></a>击穿 Hotspot Invalid</h2><blockquote><p> 缓存中的key都有对应的过期时间, 如果没有过期时间的话会导致数据的不一致, 如果热点数据过期的话, 会导致一大批的请求落到DB.</p></blockquote><ol><li>设置热点数据时尽量的将过期时间分散, 降低同一时间过期的key数量</li><li>检查热点数据是否是即将过期, 如果即将过期那么就去进行更新操作, 并延长key的过期时间.</li><li>检查key为空时, 对其他线程进行锁定, 等待该线程更新缓存后再放开.</li></ol>]]></content>
<categories>
<category>Cache</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>Pinpoint</title>
<link href="/2021/07/12/Pinpoint/"/>
<url>/2021/07/12/Pinpoint/</url>
<content type="html"><![CDATA[<p>Pinpoint是一个无侵入性的监控系统.</p><p>APM工具</p><p>低侵入性, 低性能影响</p><p>Pinpoint有三个主要组件(collector, web, agent), 并使用HBase作为存储. Collector和Web被打包为单个war文件, 而agent被打包以便可以作为java agent附加到应用.</p><ul><li>HBase (用于存储)</li><li>Pinpoint Collector (部署在web容器中)</li><li>Pinpoint Web (部署在web容器中)</li><li>Pinpoint Agent (附加到 java 应用来做采样/profile)</li></ul><h3 id="前置工具"><a href="#前置工具" class="headerlink" title="前置工具"></a>前置工具</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># git</span><br>yum install git<br><span class="hljs-comment"># docker, 可以配置阿里的镜像加速器</span><br>yum install docker<br><span class="hljs-comment"># docker-compose</span><br>curl -L https://github.com/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/<span class="hljs-built_in">local</span>/bin/docker-compose<br>chmod +x /usr/<span class="hljs-built_in">local</span>/bin/docker-compose<br></code></pre></td></tr></table></figure><h3 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h3><figure class="highlight bash"><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><code class="hljs bash"><span class="hljs-comment"># 克隆对应的仓库, 克隆慢的话可以在gitee上导入后克隆</span><br>git <span class="hljs-built_in">clone</span> https://github.com/naver/pinpoint-docker.git<br><span class="hljs-comment"># 切换到pinpoint-docker文件夹</span><br><span class="hljs-built_in">cd</span> pinpoint-docker<br><span class="hljs-comment"># 阅读Readme</span><br>docker-compose pull && docker-compose up -d<br></code></pre></td></tr></table></figure><h3 id="客户端配置"><a href="#客户端配置" class="headerlink" title="客户端配置"></a>客户端配置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 每个需要部署应用程序的服务器, 都需要在服务器上部署Agent组件</span><br><span class="hljs-comment"># 对应的版本可以在https://github.com/pinpoint-apm/pinpoint/tags找到</span><br><span class="hljs-comment"># pinpoint-agent-${version}.tar.gz下载对应版本包后, 解压, 在执行Java命令时指定对应的jar包</span><br><span class="hljs-comment"># 在java启动命令中加入如下参数</span><br>-javaagent:<span class="hljs-variable">${pinpointPath}</span>/pinpoint-bootstrap-1.8.4.jar<br>-Dpinpoint.applicationName=<br>-Dpinpoint.agentId=<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>监控</category>
</categories>
<tags>
<tag>Pinpoint</tag>
</tags>
</entry>
<entry>
<title>Redis RDB AOF</title>
<link href="/2021/07/03/Redis%20RDB%20AOF/"/>
<url>/2021/07/03/Redis%20RDB%20AOF/</url>
<content type="html"><![CDATA[<h2 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h2><h3 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h3><p>Redis运行时, RDB程序将数据库快照保存到磁盘文件中, Redis重新启动时, RDB程序可以通过载入RDB文件来还原数据库</p><p>RDB最核心的功能为rdbsave和rdbload, rdbsave用于生成RDB文件到磁盘, rdbload用于将RDB文件加载到内存中</p><p>保存RDB备份文件有手动触发和自动触发两种情况</p><ul><li>手动分为两个命令, 一个是 save, 一个是bgsave</li></ul><p>区别就是一个阻塞redis运行, 一个不阻塞</p><p>save => 直接调用rdbsave, 阻塞redis主进程, 直到RDB结束, redis才能响应客户端发来的请求</p><p>bgsave => fork出一个子进程, 子进程负责调用rdbsave, 在完成之后向主进程发送信号, 也就是说只会有创建子线程的时候有短暂的阻塞</p><ul><li>自动触发</li></ul><p>三种情况</p><p>redis m n, 这个是redis的配置, 意思是在m秒内, 有n个键发生了改变, 就触发自动化</p><p>flushall, 在清空db时触发</p><p>最后一种是在主从复制的情况下, 从节点执行全量复制的情况下, 主节点进行bgsave, 并且将RDB文件发送给从节点</p><figure class="highlight bash"><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><code class="hljs bash"><span class="hljs-comment"># 备份文件路径</span><br>dir ./<br><span class="hljs-comment"># 备份文件名</span><br>dbfilename name.rdb<br><span class="hljs-comment"># 触发条件</span><br>save 900 1<br><span class="hljs-comment"># 是否对磁盘中的快照进行压缩, 默认yes</span><br>rdbcompression yes<br><span class="hljs-comment"># 对快照进行验证, 默认yes</span><br>rdbchecksum yes<br></code></pre></td></tr></table></figure><h4 id="弊端"><a href="#弊端" class="headerlink" title="弊端"></a><strong>弊端</strong></h4><p>数据的完整性并不高, 备份时会创建一个子进程或者阻塞</p><h3 id="RDB文件结构"><a href="#RDB文件结构" class="headerlink" title="RDB文件结构"></a>RDB文件结构</h3><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs jboss-cli">+<span class="hljs-params">-------</span>+<span class="hljs-params">-------------</span>+<span class="hljs-params">-----------</span>+<span class="hljs-params">-----------------</span>+<span class="hljs-params">-----</span>+<span class="hljs-params">-----------</span>+<br>| REDIS | RDB-VERSION | SELECT-DB | KEY-VALUE-PAIRS | EOF | CHECK-SUM |<br>+<span class="hljs-params">-------</span>+<span class="hljs-params">-------------</span>+<span class="hljs-params">-----------</span>+<span class="hljs-params">-----------------</span>+<span class="hljs-params">-----</span>+<span class="hljs-params">-----------</span>+<br> |<<span class="hljs-params">--------</span> DB-DATA <span class="hljs-params">----------</span>>|<br></code></pre></td></tr></table></figure><p>REDIS => 文件最开头保存REDIS五个字符, 读入时可以检查文件的前五个字节判断是否是RDB文件.</p><p>RDB-VERSION => 四字节的字符整数, 记录该文件所用的RDB版本号 (0006), 因为不同版本的RDB文件不兼容, 所以在读入时需要判断版本确认读入方式</p><p>SELECT-DB => 对应的数据库位置</p><p>EOF => 标志数据库内容的结尾</p><p>CHECK-SUM => RDB文件所有内容的校验和, 一个 <code>uint_64t</code> 类型值, 读取时需要根据这个值进行校验, 如果值为0, 那么代表redis关闭了校验和</p><p>DB-DATA => 这个部分会出现多次, 且每个上都保存着一个非空数据库的所有数据</p><blockquote><p>为了避免产生竞争条件, BGSAVE执行时, SAVE命令不能执行.</p></blockquote><h2 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h2><p>以协议文本的方式, 将所有对数据库进行写入的命令记录</p><h3 id="AOF同步"><a href="#AOF同步" class="headerlink" title="AOF同步"></a>AOF同步</h3><p>把命令同步到AOF文件有三个阶段: </p><ol><li>命令传播: Redis将执行完的命令,命令的参数,命令的参数个数等信息发送到 AOF 程序中.</li><li>缓存追加: AOF程序根据接收到的命令数据, 将命令转换为网络通讯协议的格式, 然后将协议内容追加到服务器的 AOF 缓存中.</li><li>文件写入和保存: AOF缓存中的内容被写入到AOF文件末尾, 如果设定的AOF保存条件被满足的话, <code>fsync</code> 函数或者 <code>fdatasync</code> 函数会被调用, 将写入的内容真正地保存到磁盘中.</li></ol><blockquote><p>如果redis运行的时间久了, 这个时候AOF文件肯定也会累积的特别大, 所以就有了AOF重写.</p></blockquote><h3 id="AOF重写"><a href="#AOF重写" class="headerlink" title="AOF重写"></a>AOF重写</h3><p>本质就是将多个key的操作进行处理, 最终用正确的命令保证当前值一致</p><p>具体可以参考该网址: <a href="https://redisbook.readthedocs.io/en/latest/internal/aof.html#id12">https://redisbook.readthedocs.io/en/latest/internal/aof.html#id12</a></p>]]></content>
<categories>
<category>Cache</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>Redis 管道</title>
<link href="/2021/07/02/Redis%20%E7%AE%A1%E9%81%93/"/>
<url>/2021/07/02/Redis%20%E7%AE%A1%E9%81%93/</url>
<content type="html"><![CDATA[<h2 id="Pipelining-管道"><a href="#Pipelining-管道" class="headerlink" title="Pipelining (管道)"></a>Pipelining (管道)</h2><h4 id="正常获取"><a href="#正常获取" class="headerlink" title="正常获取"></a>正常获取</h4><p><img src="/imgs/redis_pipelining.png" alt="image-20210706160546758"></p><h4 id="通过管道获取"><a href="#通过管道获取" class="headerlink" title="通过管道获取"></a>通过管道获取</h4><p><img src="/imgs/redis_piprlining_2.png" alt="image-20210706171059372"></p><p>其实从两个图的区别就能看出来, 一次将所有命令带过去, 减少了RTT(Round Trip Time - 往返时间)的开销, 间接提高了性能.</p><p>这样可以获得更高效的处理, 但是也只能适合在部分场景使用, 需要及时获取到value的场景依旧无法使用该种方法.</p>]]></content>
<categories>
<category>Cache</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>Redis 发布 订阅</title>
<link href="/2021/06/29/Redis%20%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85/"/>
<url>/2021/06/29/Redis%20%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85/</url>
<content type="html"><![CDATA[<p> Pub/Sub实现了发布/订阅消息传递范式, Streams实现了一组阻塞操作, 允许消费者等待生产者添加到流中的新数据, 此外还有一个叫做<strong>Consumer Groups</strong>的概念, 消费者组最初是由流行的消息传递系统Kafka引入的. Redis用完全不同的术语重新实现了一个类似的想法, 但目标是相同的: 允许一组客户端合作使用同一消息流的不同部分.</p><p>一般情况下, 项目不太会采用redis来实现消息队列, 最好采用专业的消息队列处理</p><h2 id="Pub-Sub-发布-订阅"><a href="#Pub-Sub-发布-订阅" class="headerlink" title="Pub/Sub (发布 订阅)"></a>Pub/Sub (发布 订阅)</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 监听</span><br>> SUBSCRIBE one two<br>1) <span class="hljs-string">"psubscribe"</span><br>2) <span class="hljs-string">"one"</span><br>3) (<span class="hljs-built_in">integer</span>) 1<br>1) <span class="hljs-string">"psubscribe"</span><br>2) <span class="hljs-string">"two"</span><br>3) (<span class="hljs-built_in">integer</span>) 2<br><span class="hljs-comment"># 这里是监听到的消息</span><br>1) <span class="hljs-string">"message"</span><br>2) <span class="hljs-string">"two"</span><br>3) <span class="hljs-string">"msg"</span><br></code></pre></td></tr></table></figure><blockquote><p>注意监听多个队列可以空格空开, 对应的队列名也可以使用模糊匹配</p><p>第一个元素: 订阅模式</p><p>第二个元素: 队列名称</p><p>第三个元素: 对应的消息内容</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 推送</span><br>> PUBLISH one msg<br><span class="hljs-comment"># 返回值为有多少个客户端监听到了这条消息</span><br>(<span class="hljs-built_in">integer</span>) 1<br></code></pre></td></tr></table></figure><blockquote><p>这种形式实现消息队列, 可以实现重复消费, 但是没有ACK机制.</p><ol><li>redis宕机的话, 重启后这些队列中的数据是不会恢复的, 因为这些消息是不持久化消息的.</li><li>消费者宕机, 消费者宕机重启的话也一样会丢失掉对应的数据.</li></ol></blockquote><p>注意, redis对于每个客户端输出缓冲区都是有对应的限制的, 如果数据量大的话可能会导致强制关闭连接 (不止pub/sub, slave和普通客户端都可以通过该配置设置)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">client-output-buffer-limit pubsub 32mb 8mb 60<br></code></pre></td></tr></table></figure><h2 id="Stream"><a href="#Stream" class="headerlink" title="Stream"></a>Stream</h2><p>在redis5的版本更新了对应的Stream, 一个更加强大的发布订阅系统.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 新增stream, *代表自动生成条目ID, redis会按照这种格式生成<millisecondsTime>-<sequenceNumber></span><br>> XADD <span class="hljs-built_in">test</span> * name lz age 19<br><span class="hljs-comment"># 条目ID</span><br>1625385499661-0<br><span class="hljs-comment"># COUNT标志读取几条, 0-0代表从头开始找, 可以写为对应的ID, 没有消息时返回null</span><br>> XREAD COUNT 1 STREAMS <span class="hljs-built_in">test</span> 0-0<br>1) 1) <span class="hljs-string">"test"</span><br> 2) 1) 1) <span class="hljs-string">"1625385499661-0"</span><br> 2) 1) <span class="hljs-string">"name"</span><br> 2) <span class="hljs-string">"lz"</span><br> 3) <span class="hljs-string">"age"</span><br> 4) <span class="hljs-string">"19"</span><br></code></pre></td></tr></table></figure><blockquote><p><millisecondsTime>-<sequenceNumber>这种时间戳的设计是为了让用户可以根据ID进行范围查询</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 范围查询, - +代表最小ID和最大ID, count代表获取多少条</span><br>> xrange <span class="hljs-built_in">test</span> - + COUNT 2<br></code></pre></td></tr></table></figure><h3 id="阻塞式的拉取消息"><a href="#阻塞式的拉取消息" class="headerlink" title="阻塞式的拉取消息"></a>阻塞式的拉取消息</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 通过BLOCK设置阻塞等待时间, 0为不设置超时时间, 最好设置一个服务可接受的超市时间</span><br>> XREAD COUNT 5 BLOCK 0 STREAMS <span class="hljs-built_in">test</span> 1625385499661-0<br></code></pre></td></tr></table></figure><h3 id="发布订阅"><a href="#发布订阅" class="headerlink" title="发布订阅"></a>发布订阅</h3><p>stream有对应的消费者组概念, 可以通过<strong>XGROUP</strong>,<strong>XREADGROUP</strong>创建消费者组和拉取消息</p><p>假设现在有名为test的stream, 其中有一条消息, 注意是有一条消息的.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">> XGROUP CREATE <span class="hljs-built_in">test</span> testGroupOne 0<br>OK<br>> XGROUP CREATE <span class="hljs-built_in">test</span> testGroupTwo $<br>OK<br></code></pre></td></tr></table></figure><p>创建好后开始消费</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 消费者组一可以拿到对应的消息, LZ代表消费者</span><br>> XREADGROUP GROUP testGroupOne LZ COUNT 5 STREAMS queue ><br>1) 1) <span class="hljs-string">"test"</span><br> 2) 1) 1) <span class="hljs-string">"1625385499661-0"</span><br> 2) 1) <span class="hljs-string">"name"</span><br> 2) <span class="hljs-string">"lz"</span><br> 3) <span class="hljs-string">"age"</span><br> 4) <span class="hljs-string">"19"</span><br><span class="hljs-comment"># 消费者组二没有拿到对应的消息</span><br>> XREADGROUP GROUP testGroupTwo LZ COUNT 5 STREAMS queue ><br>(nil)<br></code></pre></td></tr></table></figure><blockquote><p> 在上一步创建对应的消费者组时, 对应的$参数代表着从现在开始, 到达的消息才会被传递, 而0代表着所有的消息都会被传递.</p></blockquote><blockquote><p><strong>XREADGROUP结尾的STREAMS为什么是>, 而不是对应的ID?</strong></p><p>代表着消息到目前为止从未传递给其他消费者. 注意这里, 如果有多条未被阅读的消息, 会读取所有的.</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs bash">> XADD <span class="hljs-built_in">test</span> * name lz age 19<br>1625385499661-0<br><span class="hljs-comment"># 创建两个消费者组, 绑定test</span><br>> XGROUP CREATE <span class="hljs-built_in">test</span> testGroupOne 0<br>OK<br>> XGROUP CREATE <span class="hljs-built_in">test</span> testGroupTwo 0<br>OK<br>> XREADGROUP GROUP testGroupOne LZ COUNT 5 STREAMS queue ><br>1) 1) <span class="hljs-string">"test"</span><br> 2) 1) 1) <span class="hljs-string">"1625385499661-0"</span><br> 2) 1) <span class="hljs-string">"name"</span><br> 2) <span class="hljs-string">"lz"</span><br> 3) <span class="hljs-string">"age"</span><br> 4) <span class="hljs-string">"19"</span><br>> XREADGROUP GROUP testGroupTwo LZ COUNT 5 STREAMS queue ><br>1) 1) <span class="hljs-string">"test"</span><br> 2) 1) 1) <span class="hljs-string">"1625385499661-0"</span><br> 2) 1) <span class="hljs-string">"name"</span><br> 2) <span class="hljs-string">"lz"</span><br> 3) <span class="hljs-string">"age"</span><br> 4) <span class="hljs-string">"19"</span><br></code></pre></td></tr></table></figure><p>可以看到两组消费者都可以对一条消息进行消费.</p><h3 id="消息丢失"><a href="#消息丢失" class="headerlink" title="消息丢失"></a>消息丢失</h3><p>如果消费者宕机了, 就不会发出ACK请求, 这个消息依旧会被redis保留</p><h3 id="stream丢失"><a href="#stream丢失" class="headerlink" title="stream丢失"></a>stream丢失</h3><p>stream算是一种新增加的数据类型, 会被写入到AOF和RDB.</p><h3 id="消息堆积的处理"><a href="#消息堆积的处理" class="headerlink" title="消息堆积的处理"></a>消息堆积的处理</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">> XADD <span class="hljs-built_in">test</span> MAXLEN ~ 1000 * name lz age 19<br></code></pre></td></tr></table></figure><p>这样的话当长度超出上限之后, 旧的消息会被移除掉, 但是这样也有可能导致消息丢失</p><p>注意这个 <strong>~</strong> 符号可以不带, 这个参数标志着不需要精确到1000, 只要最少是1000就可以. 因为stream的实现原因, 这个参数可以让实现更高效.</p>]]></content>
<categories>
<category>Cache</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>Redis 类型</title>
<link href="/2021/06/29/Redis%20%E7%B1%BB%E5%9E%8B/"/>
<url>/2021/06/29/Redis%20%E7%B1%BB%E5%9E%8B/</url>
<content type="html"><![CDATA[<p>redis是一个开源(BSD许可)的内存型数据结构存储系统, 可以被作为缓存, 数据库, 消息中间件等;</p><p>支持了多种类型的数据类型, Strings,Hashes,Lists,Sets,Sorted Sets,BitMaps,hyperloglogs,地理空间(geospatial)索引半径查询</p><p>官网地址: <a href="https://redis.io/">https://redis.io/</a></p><p>中文地址: <a href="http://redis.cn/">http://redis.cn/</a></p><h2 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h2><h4 id="String"><a href="#String" class="headerlink" title="String"></a>String</h4><p>可以包含任何数据, 存储的是二进制数据</p><p>普通数据缓存 / 计数器 / 分布式锁 / 分布式ID 等</p><h4 id="List"><a href="#List" class="headerlink" title="List"></a>List</h4><figure class="highlight c"><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><code class="hljs c"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">listNode</span> {</span><br> <span class="hljs-comment">// 前置节点</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">listNode</span> *<span class="hljs-title">prev</span>;</span><br> <span class="hljs-comment">// 后置节点</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">listNode</span> *<span class="hljs-title">next</span>;</span><br> <span class="hljs-comment">// 节点的值</span><br> <span class="hljs-keyword">void</span> *value;<br>} listNode;<br></code></pre></td></tr></table></figure><p>每个节点上都保存了对应的上下级关系, 所以是一个双端列表</p><p><strong>表头的前置节点</strong>和<strong>表尾的后置节点</strong>都指向了null, 实现是一个无环链表</p><p>栈, 队列, 数组, 阻塞单播队列</p><figure class="highlight bash"><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><code class="hljs bash"><span class="hljs-comment"># 生产者</span><br>> LPUSH msgQueue msg<br><span class="hljs-comment"># 消费者 阻塞获取消息, 0为无限期的阻塞, 注意这里的0被redis认为是double值, 注意可以同时阻塞多个list</span><br>> BRPOP msgQueue 0<br><span class="hljs-string">"msg"</span><br></code></pre></td></tr></table></figure><blockquote><p> 这样可以实现一个简单的消息队列, 使用redis的阻塞式获取消息的方式, 但是注意这种情况会有连接失效的可能性, 尽量不要使用无限期阻塞, 一旦无限期阻塞连接断开后线程被阻塞, 无法进行重新连接, 最好设置一定时间处理, 这样即使连接断开了, 也会在下一次重新执行BRPOP的时候检查连接状态.</p><p>缺点: <strong>无法重复消费</strong>, 一旦弹出, 这个数据就在list中移除掉了.</p></blockquote><h4 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h4><p>类似于map的结构</p><p>适合用来存储对象</p><h4 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h4><p>无序, 去重, 随机事件</p><h4 id="Sorted-Set"><a href="#Sorted-Set" class="headerlink" title="Sorted Set"></a>Sorted Set</h4><p>于Set类似, 只多了一个score(分值), 虽然值不允许, 但是score允许重复.</p><p>底层结构有两种</p><blockquote><p> <strong>ziplist</strong></p></blockquote><p>在满足以下条件的时候使用ziplist</p><ol><li>有序集合保存的元素数量小于128个</li><li>有序集合保存的所有元素的长度小于64字节</li></ol><p>可以在redis.conf中进行配置</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">zset</span>-max-ziplist-entries <span class="hljs-number">128</span><br><span class="hljs-attribute">zset</span>-max-ziplist-value <span class="hljs-number">64</span><br></code></pre></td></tr></table></figure><blockquote><p><strong>skiplist</strong></p></blockquote><p><img src="/imgs/skiplist.png"></p><p>跳表是基于有序链表的拓展, 要维护原链表和多个索引链表.</p><p>最左边的是zskiplist结构:</p><ul><li><code>header</code>: 指向跳跃表的表头节点.</li><li><code>tail</code>: 指向跳跃表的表尾节点.</li><li><code>level</code>: 记录目前跳跃表内, 层数最大的那个节点的层数(表头节点的层数不计算在内).</li><li><code>length</code>: 记录跳跃表的长度, 就是跳跃表目前包含节点的数量(表头节点不计算在内).</li></ul><p>右边是zskiplistNode结构:</p><ul><li>层(level): 节点中用 <code>L1</code> ,<code>L2</code>, <code>L3</code> 等字样标记节点的各个层, <code>L1</code> 代表第一层, <code>L2</code> 代表第二层, 以此类推. 每个层都带有两个属性: 前进指针和跨度. 前进指针用于访问位于表尾方向的其他节点, 而跨度则记录了前进指针所指向节点和当前节点的距离. 在上面的图片中, 连线上带有数字的箭头就代表前进指针, 而那个数字就是跨度. 当程序从表头向表尾进行遍历时, 访问会沿着层的前进指针进行.</li><li>后退(backward)指针: 节点中用 <code>BW</code> 字样标记节点的后退指针, 它指向位于当前节点的前一个节点. 后退指针在程序从表尾向表头遍历时使用.</li><li>分值(score): 各个节点中的 <code>1.0</code> , <code>2.0</code> 和 <code>3.0</code> 是节点所保存的分值. 在跳跃表中, 节点按各自所保存的分值从小到大排列.</li><li>成员对象(obj): 各个节点中的 <code>o1</code> , <code>o2</code> 和 <code>o3</code> 是节点所保存的成员对象.</li></ul><blockquote><p>注意: 表头节点和其他节点的构造是一样的: 表头节点也有对应的后退节点等, 只是这些属性不会被用到.</p></blockquote><p>可以实现类似排行榜的功能</p>]]></content>
<categories>
<category>Cache</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>HashMap</title>
<link href="/2021/06/25/HashMap/"/>
<url>/2021/06/25/HashMap/</url>
<content type="html"><![CDATA[<p>源码版本为 JDK 1.8.</p><p>HashMap 允许 null 键和 null 值, 在计算哈键的哈希值时, null哈希值为0. HashMap不保证键值对的顺序, 遍历时<strong>无序</strong>, 这也代表在进行某些操作后, 键值对的顺序可能会发生变化. 另外, 需要注意的是, HashMap 是非线程安全类, 在多线程环境下可能会存在问题.</p><h3 id="HashMap-关系"><a href="#HashMap-关系" class="headerlink" title="HashMap 关系"></a>HashMap 关系</h3><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><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HashMap</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractMap</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>></span><br><span class="hljs-class"> <span class="hljs-keyword">implements</span> <span class="hljs-title">Map</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>>, <span class="hljs-title">Cloneable</span>, <span class="hljs-title">Serializable</span> </span>{}<br></code></pre></td></tr></table></figure><h4 id="diagram"><a href="#diagram" class="headerlink" title="diagram"></a>diagram</h4><p><img src="/imgs/HashMap_diagram.jpg"></p><h3 id="重要参数"><a href="#重要参数" class="headerlink" title="重要参数"></a>重要参数</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 初始容量</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="hljs-number">1</span> << <span class="hljs-number">4</span>;<br><span class="hljs-comment">// 最大容量</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MAXIMUM_CAPACITY = <span class="hljs-number">1</span> << <span class="hljs-number">30</span>;<br><span class="hljs-comment">// 负载因子</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> DEFAULT_LOAD_FACTOR = <span class="hljs-number">0.75f</span>;<br><span class="hljs-comment">// 链表长度大于TREEIFY_THRESHOLD时转为红黑树</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> TREEIFY_THRESHOLD = <span class="hljs-number">8</span>;<br><span class="hljs-comment">// 链表长度小于UNTREEIFY_THRESHOLD时转为链表</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> UNTREEIFY_THRESHOLD = <span class="hljs-number">6</span>;<br><span class="hljs-comment">// 可被树化的最小容量</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MIN_TREEIFY_CAPACITY = <span class="hljs-number">64</span>;<br><span class="hljs-comment">// 存储元素的数组</span><br><span class="hljs-keyword">transient</span> Node<k,v>[] table;<br><span class="hljs-comment">// 缓存存放</span><br><span class="hljs-keyword">transient</span> Set<map.entry<k,v>> entrySet;<br><span class="hljs-comment">// 键值映射关系数量</span><br><span class="hljs-keyword">transient</span> <span class="hljs-keyword">int</span> size;<br><span class="hljs-comment">// 临界值 当实际的KV个数超过它时, 进行扩容</span><br><span class="hljs-keyword">int</span> threshold;<br><span class="hljs-comment">// 哈希表的加载因子</span><br><span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> loadFactor;<br></code></pre></td></tr></table></figure><p>HashMap内部类Node<K, V></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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>> <span class="hljs-keyword">implements</span> <span class="hljs-title">Map</span>.<span class="hljs-title">Entry</span><<span class="hljs-title">K</span>,<span class="hljs-title">V</span>> </span>{<br> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> hash;<br> <span class="hljs-keyword">final</span> K key;<br> V value;<br> Node<K,V> next;<br><br> Node(<span class="hljs-keyword">int</span> hash, K key, V value, Node<K,V> next) {<br> <span class="hljs-keyword">this</span>.hash = hash;<br> <span class="hljs-keyword">this</span>.key = key;<br> <span class="hljs-keyword">this</span>.value = value;<br> <span class="hljs-keyword">this</span>.next = next;<br> }<br><br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> K <span class="hljs-title">getKey</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> key; }<br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> V <span class="hljs-title">getValue</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> value; }<br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> key + <span class="hljs-string">"="</span> + value; }<br><br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">return</span> Objects.hashCode(key) ^ Objects.hashCode(value);<br> }<br><br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> V <span class="hljs-title">setValue</span><span class="hljs-params">(V newValue)</span> </span>{<br> V oldValue = value;<br> value = newValue;<br> <span class="hljs-keyword">return</span> oldValue;<br> }<br><br> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object o)</span> </span>{<br> <span class="hljs-keyword">if</span> (o == <span class="hljs-keyword">this</span>)<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br> <span class="hljs-keyword">if</span> (o <span class="hljs-keyword">instanceof</span> Map.Entry) {<br> Map.Entry<?,?> e = (Map.Entry<?,?>)o;<br> <span class="hljs-keyword">if</span> (Objects.equals(key, e.getKey()) &&<br> Objects.equals(value, e.getValue()))<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h3><h4 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> V <span class="hljs-title">put</span><span class="hljs-params">(K key, V value)</span> </span>{<br> <span class="hljs-keyword">return</span> putVal(hash(key), key, value, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">true</span>);<br>}<br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> <span class="hljs-title">hash</span><span class="hljs-params">(Object key)</span> </span>{<br> <span class="hljs-keyword">int</span> h;<br> <span class="hljs-keyword">return</span> (key == <span class="hljs-keyword">null</span>) ? <span class="hljs-number">0</span> : (h = key.hashCode()) ^ (h >>> <span class="hljs-number">16</span>);<br>}<br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 注意: 部分变量的赋值是在if判断中</span><br><span class="hljs-comment"> * onlyIfAbsent == true时不修改现有值</span><br><span class="hljs-comment"> * evict == false时证明处于创建模式</span><br><span class="hljs-comment"> */</span><br><span class="hljs-function"><span class="hljs-keyword">final</span> V <span class="hljs-title">putVal</span><span class="hljs-params">(<span class="hljs-keyword">int</span> hash, K key, V value, <span class="hljs-keyword">boolean</span> onlyIfAbsent,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">boolean</span> evict)</span> </span>{<br> Node<K,V>[] tab; Node<K,V> p; <span class="hljs-keyword">int</span> n, i;<br> <span class="hljs-keyword">if</span> ((tab = table) == <span class="hljs-keyword">null</span> || (n = tab.length) == <span class="hljs-number">0</span>)<br> <span class="hljs-comment">// 初始化</span><br> n = (tab = resize()).length;<br> <span class="hljs-keyword">if</span> ((p = tab[i = (n - <span class="hljs-number">1</span>) & hash]) == <span class="hljs-keyword">null</span>)<br> <span class="hljs-comment">// 确认该位置为空, 则放入对应值</span><br> tab[i] = newNode(hash, key, value, <span class="hljs-keyword">null</span>);<br> <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 进到这里, 有可能覆盖值, 或者在后面添加新的Node</span><br> Node<K,V> e; K k;<br> <span class="hljs-comment">// 如果hash一样, 且key一样</span><br> <span class="hljs-keyword">if</span> (p.hash == hash &&<br> ((k = p.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k))))<br> <span class="hljs-comment">// 获取到对应元素赋值</span><br> e = p;<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (p <span class="hljs-keyword">instanceof</span> TreeNode)<br> <span class="hljs-comment">// 如果p是树节点, 那么就直接使用对应的putTreeVal方法插入新节点</span><br> e = ((TreeNode<K,V>)p).putTreeVal(<span class="hljs-keyword">this</span>, tab, hash, key, value);<br> <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> binCount = <span class="hljs-number">0</span>; ; ++binCount) {<br> <span class="hljs-keyword">if</span> ((e = p.next) == <span class="hljs-keyword">null</span>) {<br> p.next = newNode(hash, key, value, <span class="hljs-keyword">null</span>);<br> <span class="hljs-keyword">if</span> (binCount >= TREEIFY_THRESHOLD - <span class="hljs-number">1</span>) <span class="hljs-comment">// -1 for 1st</span><br> treeifyBin(tab, hash);<br> <span class="hljs-keyword">break</span>;<br> }<br> <span class="hljs-keyword">if</span> (e.hash == hash &&<br> ((k = e.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k))))<br> <span class="hljs-keyword">break</span>;<br> p = e;<br> }<br> }<br> <span class="hljs-keyword">if</span> (e != <span class="hljs-keyword">null</span>) {<br> <span class="hljs-comment">// 将老的值拿出来</span><br> V oldValue = e.value;<br> <span class="hljs-comment">// 确认是否需要覆盖</span><br> <span class="hljs-keyword">if</span> (!onlyIfAbsent || oldValue == <span class="hljs-keyword">null</span>)<br> <span class="hljs-comment">// 覆盖oldValue</span><br> e.value = value;<br> afterNodeAccess(e);<br> <span class="hljs-comment">// 返回oldValue, 证明没有覆盖其他值</span><br> <span class="hljs-keyword">return</span> oldValue;<br> }<br> }<br> ++modCount;<br> <span class="hljs-comment">// 判断是否大于临界值, 大于的话就进行扩容</span><br> <span class="hljs-keyword">if</span> (++size > threshold)<br> resize();<br> <span class="hljs-comment">// 该方法为LinkedHashMap(继承HashMap)服务</span><br> afterNodeInsertion(evict);<br> <span class="hljs-comment">// 返回null, 证明没有覆盖其他值</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>}<br><span class="hljs-keyword">final</span> Node<K,V>[] resize() {<br> Node<K,V>[] oldTab = table;<br> <span class="hljs-comment">// oldCap = 0时代表第一次扩容</span><br> <span class="hljs-keyword">int</span> oldCap = (oldTab == <span class="hljs-keyword">null</span>) ? <span class="hljs-number">0</span> : oldTab.length;<br> <span class="hljs-keyword">int</span> oldThr = threshold;<br> <span class="hljs-keyword">int</span> newCap, newThr = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">if</span> (oldCap > <span class="hljs-number">0</span>) {<br> <span class="hljs-comment">// 大于最大值</span><br> <span class="hljs-keyword">if</span> (oldCap >= MAXIMUM_CAPACITY) {<br> <span class="hljs-comment">// 将Integer最大值赋值到threshold</span><br> threshold = Integer.MAX_VALUE;<br> <span class="hljs-comment">// 返回原始table</span><br> <span class="hljs-keyword">return</span> oldTab;<br> }<br> <span class="hljs-comment">// 小于最大值且原先容量大于默认容量</span><br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((newCap = oldCap << <span class="hljs-number">1</span>) < MAXIMUM_CAPACITY &&<br> oldCap >= DEFAULT_INITIAL_CAPACITY)<br> <span class="hljs-comment">// 相当于乘2</span><br> newThr = oldThr << <span class="hljs-number">1</span>;<br> }<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (oldThr > <span class="hljs-number">0</span>)<br> <span class="hljs-comment">// 新的容量等于老的临界值</span><br> newCap = oldThr;<br> <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 初始化, 使用默认值</span><br> newCap = DEFAULT_INITIAL_CAPACITY;<br> newThr = (<span class="hljs-keyword">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);<br> }<br> <span class="hljs-comment">// 新的临界值 == 0时需要重新计算</span><br> <span class="hljs-keyword">if</span> (newThr == <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">float</span> ft = (<span class="hljs-keyword">float</span>)newCap * loadFactor;<br> newThr = (newCap < MAXIMUM_CAPACITY && ft < (<span class="hljs-keyword">float</span>)MAXIMUM_CAPACITY ?<br> (<span class="hljs-keyword">int</span>)ft : Integer.MAX_VALUE);<br> }<br> threshold = newThr;<br> <span class="hljs-meta">@SuppressWarnings({"rawtypes","unchecked"})</span><br> <span class="hljs-comment">// 创建新的数组</span><br> Node<K,V>[] newTab = (Node<K,V>[])<span class="hljs-keyword">new</span> Node[newCap];<br> table = newTab;<br> <span class="hljs-keyword">if</span> (oldTab != <span class="hljs-keyword">null</span>) {<br> <span class="hljs-comment">// 遍历处理原数据</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j < oldCap; ++j) {<br> Node<K,V> e;<br> <span class="hljs-comment">// 为null跳过</span><br> <span class="hljs-keyword">if</span> ((e = oldTab[j]) != <span class="hljs-keyword">null</span>) {<br> oldTab[j] = <span class="hljs-keyword">null</span>;<br> <span class="hljs-keyword">if</span> (e.next == <span class="hljs-keyword">null</span>)<br> <span class="hljs-comment">// 重新计算位置</span><br> newTab[e.hash & (newCap - <span class="hljs-number">1</span>)] = e;<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> TreeNode)<br> ((TreeNode<K,V>)e).split(<span class="hljs-keyword">this</span>, newTab, j, oldCap);<br> <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 保证顺序</span><br> Node<K,V> loHead = <span class="hljs-keyword">null</span>, loTail = <span class="hljs-keyword">null</span>;<br> Node<K,V> hiHead = <span class="hljs-keyword">null</span>, hiTail = <span class="hljs-keyword">null</span>;<br> Node<K,V> next;<br> <span class="hljs-keyword">do</span> {<br> next = e.next;<br> <span class="hljs-keyword">if</span> ((e.hash & oldCap) == <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">if</span> (loTail == <span class="hljs-keyword">null</span>)<br> loHead = e;<br> <span class="hljs-keyword">else</span><br> loTail.next = e;<br> loTail = e;<br> }<br> <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">if</span> (hiTail == <span class="hljs-keyword">null</span>)<br> hiHead = e;<br> <span class="hljs-keyword">else</span><br> hiTail.next = e;<br> hiTail = e;<br> }<br> } <span class="hljs-keyword">while</span> ((e = next) != <span class="hljs-keyword">null</span>);<br> <span class="hljs-keyword">if</span> (loTail != <span class="hljs-keyword">null</span>) {<br> loTail.next = <span class="hljs-keyword">null</span>;<br> newTab[j] = loHead;<br> }<br> <span class="hljs-keyword">if</span> (hiTail != <span class="hljs-keyword">null</span>) {<br> hiTail.next = <span class="hljs-keyword">null</span>;<br> newTab[j + oldCap] = hiHead;<br> }<br> }<br> }<br> }<br> }<br> <span class="hljs-keyword">return</span> newTab;<br>}<br><span class="hljs-function">Node<K,V> <span class="hljs-title">newNode</span><span class="hljs-params">(<span class="hljs-keyword">int</span> hash, K key, V value, Node<K,V> next)</span> </span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Node<>(hash, key, value, next);<br>}<br><span class="hljs-comment">// 将链表转为红黑树</span><br><span class="hljs-function"><span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">treeifyBin</span><span class="hljs-params">(Node<K,V>[] tab, <span class="hljs-keyword">int</span> hash)</span> </span>{<br> <span class="hljs-keyword">int</span> n, index; Node<K,V> e;<br> <span class="hljs-keyword">if</span> (tab == <span class="hljs-keyword">null</span> || (n = tab.length) < MIN_TREEIFY_CAPACITY)<br> resize();<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((e = tab[index = (n - <span class="hljs-number">1</span>) & hash]) != <span class="hljs-keyword">null</span>) {<br> TreeNode<K,V> hd = <span class="hljs-keyword">null</span>, tl = <span class="hljs-keyword">null</span>;<br> <span class="hljs-keyword">do</span> {<br> TreeNode<K,V> p = replacementTreeNode(e, <span class="hljs-keyword">null</span>);<br> <span class="hljs-keyword">if</span> (tl == <span class="hljs-keyword">null</span>)<br> hd = p;<br> <span class="hljs-keyword">else</span> {<br> p.prev = tl;<br> tl.next = p;<br> }<br> tl = p;<br> } <span class="hljs-keyword">while</span> ((e = e.next) != <span class="hljs-keyword">null</span>);<br> <span class="hljs-keyword">if</span> ((tab[index] = hd) != <span class="hljs-keyword">null</span>)<br> hd.treeify(tab);<br> }<br>}<br><span class="hljs-function">TreeNode<K,V> <span class="hljs-title">replacementTreeNode</span><span class="hljs-params">(Node<K,V> p, Node<K,V> next)</span> </span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeNode<>(p.hash, p.key, p.value, next);<br>}<br></code></pre></td></tr></table></figure><h4 id="获取"><a href="#获取" class="headerlink" title="获取"></a>获取</h4><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> V <span class="hljs-title">get</span><span class="hljs-params">(Object key)</span> </span>{<br> Node<K,V> e;<br> <span class="hljs-keyword">return</span> (e = getNode(hash(key), key)) == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">null</span> : e.value;<br>}<br><span class="hljs-function"><span class="hljs-keyword">final</span> Node<K,V> <span class="hljs-title">getNode</span><span class="hljs-params">(<span class="hljs-keyword">int</span> hash, Object key)</span> </span>{<br> Node<K,V>[] tab; Node<K,V> first, e; <span class="hljs-keyword">int</span> n; K k;<br> <span class="hljs-comment">// 确认table不为空, 并且确认table中是否存在这个节点</span><br> <span class="hljs-keyword">if</span> ((tab = table) != <span class="hljs-keyword">null</span> && (n = tab.length) > <span class="hljs-number">0</span> &&<br> (first = tab[(n - <span class="hljs-number">1</span>) & hash]) != <span class="hljs-keyword">null</span>) {<br> <span class="hljs-comment">// 检查第一个节点</span><br> <span class="hljs-keyword">if</span> (first.hash == hash &&<br> ((k = first.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k))))<br> <span class="hljs-keyword">return</span> first;<br> <span class="hljs-keyword">if</span> ((e = first.next) != <span class="hljs-keyword">null</span>) {<br> <span class="hljs-keyword">if</span> (first <span class="hljs-keyword">instanceof</span> TreeNode)<br> <span class="hljs-comment">// 树结构检索</span><br> <span class="hljs-keyword">return</span> ((TreeNode<K,V>)first).getTreeNode(hash, key);<br> <span class="hljs-comment">// 链表检索</span><br> <span class="hljs-keyword">do</span> {<br> <span class="hljs-keyword">if</span> (e.hash == hash &&<br> ((k = e.key) == key || (key != <span class="hljs-keyword">null</span> && key.equals(k))))<br> <span class="hljs-keyword">return</span> e;<br> } <span class="hljs-keyword">while</span> ((e = e.next) != <span class="hljs-keyword">null</span>);<br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>}<br></code></pre></td></tr></table></figure><blockquote><p><strong>为什么Map桶中超过8个元素才会转化为红黑树?</strong></p><p>源码中其实也有解释, TreeNode大概是常规的节点两倍大小, 考虑到时间空间平衡, 最早使用链表, 空间占用较少, 且查询也不会特别慢, 但是链表变长后会导致查询变慢, 所以转换为红黑树保证查询的效率. 如果hash分布良好的话, 其实红黑树这种形式应该是很少会被使用到的, 一般不会存在链表长度能够到达8的情况. 对于转换的值默认为8, 源码中也有对应的解释.</p></blockquote><blockquote><p><strong>KEY可以为null吗? VALUE是否可以为null?</strong></p><p>都可以, 但是key只能有一个null(重复的key会被覆盖), 但是value可以拥有多个.</p><p><strong>一般用什么作为KEY?</strong></p><p>一般为Integer,String这种不可变的值作为对应的KEY, 因为将一个可变类作为KEY会导致hashcode发生变化.</p></blockquote><blockquote><p><strong>多线程使用HashMap会有什么问题?</strong></p><p>同时put可能会导致元素丢失. 这个问题必要容易理解, 两个线程同时向一个位置进行插入, 最终肯定只会剩下一个值, 另一个只会被覆盖掉.</p><p>put时get导致返回null, 在put方法中有这么一个操作<code>oldTab[j] = null;</code>, 将table[j]置为null, 这个时候如果get刚好是这个位置, 会导致获取到null.</p></blockquote>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>数据结构</tag>
<tag>MAP</tag>
</tags>
</entry>
<entry>
<title>JVM垃圾回收算法</title>
<link href="/2021/06/24/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95/"/>
<url>/2021/06/24/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95/</url>
<content type="html"><![CDATA[<h3 id="标记清除算法"><a href="#标记清除算法" class="headerlink" title="标记清除算法"></a>标记清除算法</h3><p><code>Mark-Sweep(标记-清除)算法</code>有<code>标记</code>和<code>清除</code>两个阶段.</p><p>首先标记出活动(可达对象, reachable)的对象, 标记完成后统一回收所有没有标记的对象.</p><p><img src="/imgs/%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4.png"></p><p>从这个图上可以看出, 实现相对简单, 但是问题在于会产生内存碎片, 碎片太多容易导致后续过程中需要为大对象分配空间时, 无法找到足够的空间而再次触发垃圾回收</p><p>也就是说标记-清除算法不需要进行对象的移动, 只需对没有存活的对象进行处理, 在存活对象比较多的情况下极为高效, 但由于直接回收不存活的对象, 没有进行整理(可谓<code>一屋不扫, 何以扫天下</code>), 因此会造成内存碎片.</p><blockquote><p><strong>既然这种算法缺点如此明显, 那该怎么处理?</strong><br>为了优化这种算法, 其实想想的话, 思路无非两种, 一种是增加整理的功能, 另一种就是放弃掉这种方法, <code>标记整理</code>就是前者, <code>复制算法</code>则为后者</p></blockquote><h3 id="复制算法"><a href="#复制算法" class="headerlink" title="复制算法"></a>复制算法</h3><p>为了解决<code>Mark-Sweep(标记-清除)算法</code>的缺陷, <code>Copying(复制)算法</code>将可用内存按容量划分为大小相等的两块, 每次只使用其中一块. 当一块的内存用完了, 就将存活着的对象复制到另外一块上面, 然后再把已使用的内存空间一次清理掉, 这样一来就不容易出现内存碎片的问题.</p><p>复制算法的提出是为了克服句柄的开销和解决内存碎片的问题.</p><p><img src="/imgs/%E5%A4%8D%E5%88%B6.png"></p><p>这种算法虽然实现简单, 运行高效且不容易产生内存碎片, 但是却牺牲了极大的内存空间, 将可用内存缩减到原来的一半.开始时把堆分成<code>对象面</code>和<code>空闲面</code>, 程序从<code>对象面</code>为对象分配空间, 当<code>对象面</code>满了, 基于<code>Copying算法</code>的垃圾收集就从<code>根集合(GC Roots)</code>中扫描活动对象, 并将每个活动对象复制到<code>空闲面</code>(使得活动对象所占的内存之间没有内存碎片), 这样的话<code>空闲面</code>就变成了<code>对象面</code>, 原来的<code>对象面</code>则变成了<code>空闲面</code>, 程序会在新的<code>对象面</code>中分配内存.</p><blockquote><p><strong>Copying算法的问题难道只存在与浪费内存?</strong><br>肯定不是, Copying算法运行机制无非就是拷贝, 将A中的存活对象拷贝到B中, 但是存活对象的数目很大的话, 那么Copying算法的效率也一定会大大降低. </p></blockquote><h3 id="标记整理算法"><a href="#标记整理算法" class="headerlink" title="标记整理算法"></a>标记整理算法</h3><p>为了解决<code>Copying算法</code>的缺陷, 充分利用内存空间, 提出了<code>Mark-Compact算法</code>. 该算法标记阶段和<code>Mark-Sweep</code>一样, 但是在完成标记之后, 它不是直接清理可回收对象, 而是将存活对象都向一端移动, 然后清理掉端边界以外的内存.</p><p><code>标记整理算法</code>采用<code>标记清除算法</code>一样的方式进行对象的标记, 但在清除时不同, 是先将所有的对象整理, 并更新对应的指针后在进行清除. 也就是说多进行了对象的移动, 因此成本会更高, 但是却解决了内存碎片的问题.</p><p><img src="/imgs/%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86.png"></p><blockquote><p><strong>怎么进行整理?</strong><br>既然要移动, 那么肯定有很多中方式, 是直接将对象堆积到一起还是怎么, 肯定还有需要考虑的空间. 这个算法有很多的变种, 例子: <code>Threaded compaction</code></p></blockquote><h3 id="分代收集算法"><a href="#分代收集算法" class="headerlink" title="分代收集算法"></a>分代收集算法</h3><p><code>分代收集算法</code>是目前大部分 JVM 的垃圾收集器采用的算法. 核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域.</p><p>一般情况下将堆区划分为<code>老年代(Tenured Generation)</code>和<code>新生代(Young Generation)</code>, 在堆区之外还有一个代就是<code>永久代(Permanet Generation)</code>. 老年代的特点是每次垃圾回收时只有少量对象需要被回收, 而新生代的特点是每次垃圾回收时都有大量的对象需要被回收, 那么就可以根据不同代的特点采取适合的收集算法.</p><p>目前大部分垃圾收集器对于<code>新生代</code>都采取<code>Copying算法</code>, 因为<code>新生代</code>中每次垃圾回收都要回收大部分对象, 也就是说需要复制的操作次数较少, 但并不是按照1:1的比例来划分新生代空间的, 一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间(一般为 8:1:1), 每次使用Eden空间和其中的一块Survivor空间, 当进行回收时, 将Eden和Survivor中还存活的对象复制到另一块Survivor空间中, 然后清理掉Eden和刚才使用过的Survivor空间.</p><blockquote><p><strong>为什么需要两个Survivor, 两个Survivor有什么区别?</strong><br>两个Survivor区域分别是From区和To区, 执行年轻代的GC时, 两个Survivor的互换其实就是指针的互换.</p></blockquote><blockquote><p><strong>那么什么对象会进入老年代?</strong><br>对象里都有一个age字段, 代表对象经历的年轻代GC次数, 新创建的为0, 每次GC都会加1, 在每次GC时也都会检查对象的年龄, 如果大于AGE_MAX, 那么就会将对象放到老年代.</p></blockquote><ol><li>所有新生成的对象首先都是放在年轻代的, 年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象.</li><li>新生代内存按照8:1:1的比例分为一个Eden区和两个Survivor(survivor0, survivor1)区. 大部分对象在Eden区中生成. 回收时先将Eden区存活对象复制到一个Survivor0区, 然后清空Eden区, 当这个Survivor0区也存放满了时, 则将Eden区和Survivor0区存活对象复制到另一个Survivor1区, 然后清空Eden和这个Survivor0区, 此时Survivor0区是空的, 然后将Survivor0区和Survivor1区交换, 即保持Survivor1区为空, 如此往复. 当Eden没有足够空间的时候就会触发jvm发起一次Minor GC</li></ol><blockquote><p><strong>为什么保持Survivor1为空?</strong><br>为了让Eden和Survivor0交换存活对象</p></blockquote><ol start="3"><li>当Survivor1区不足以存放Eden和Survivor0的存活对象时, 就将存活对象直接存放到老年代. 若是老年代也满了就会触发一次Full GC(Major GC), 也就是新生代,老年代都进行回收.</li><li>新生代发生的GC也叫做Minor GC, MinorGC发生频率比较高(不一定等Eden区满了才触发).</li></ol>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>JVM</tag>
</tags>
</entry>
<entry>
<title>CopyOnWriteArrayList</title>
<link href="/2021/06/24/CopyOnWriteArrayList/"/>
<url>/2021/06/24/CopyOnWriteArrayList/</url>
<content type="html"><![CDATA[<h3 id="CopyOnWriteArrayList-关系"><a href="#CopyOnWriteArrayList-关系" class="headerlink" title="CopyOnWriteArrayList 关系"></a>CopyOnWriteArrayList 关系</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CopyOnWriteArrayList</span><<span class="hljs-title">E</span>></span><br><span class="hljs-class"> // 随机访问克隆序列化</span><br><span class="hljs-class"> <span class="hljs-keyword">implements</span> <span class="hljs-title">List</span><<span class="hljs-title">E</span>>, <span class="hljs-title">RandomAccess</span>, <span class="hljs-title">Cloneable</span>, <span class="hljs-title">java</span>.<span class="hljs-title">io</span>.<span class="hljs-title">Serializable</span> </span>{<br> <span class="hljs-comment">// 注意 transient volatile 修饰 </span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">transient</span> <span class="hljs-keyword">volatile</span> Object[] array;<br>}<br></code></pre></td></tr></table></figure><h4 id="diagram"><a href="#diagram" class="headerlink" title="diagram"></a>diagram</h4><p><img src="/imgs/CopyOnWriteArrayList_diagram.jpg"></p><h3 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h3><h4 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">final</span> <span class="hljs-keyword">transient</span> Object lock = <span class="hljs-keyword">new</span> Object();<br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">add</span><span class="hljs-params">(E e)</span> </span>{<br> <span class="hljs-comment">// 加锁</span><br> <span class="hljs-keyword">synchronized</span> (lock) {<br> Object[] es = getArray();<br> <span class="hljs-keyword">int</span> len = es.length;<br> <span class="hljs-comment">// 复制数组</span><br> es = Arrays.copyOf(es, len + <span class="hljs-number">1</span>);<br> es[len] = e;<br> setArray(es);<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br> }<br>}<br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index, E element)</span> </span>{<br> <span class="hljs-keyword">synchronized</span> (lock) {<br> Object[] es = getArray();<br> <span class="hljs-keyword">int</span> len = es.length;<br> <span class="hljs-comment">// 判断index是否越界</span><br> <span class="hljs-keyword">if</span> (index > len || index < <span class="hljs-number">0</span>)<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IndexOutOfBoundsException(outOfBounds(index, len));<br> Object[] newElements;<br> <span class="hljs-keyword">int</span> numMoved = len - index;<br> <span class="hljs-keyword">if</span> (numMoved == <span class="hljs-number">0</span>)<br> <span class="hljs-comment">// 证明向最后插入</span><br> newElements = Arrays.copyOf(es, len + <span class="hljs-number">1</span>);<br> <span class="hljs-keyword">else</span> {<br> newElements = <span class="hljs-keyword">new</span> Object[len + <span class="hljs-number">1</span>];<br> System.arraycopy(es, <span class="hljs-number">0</span>, newElements, <span class="hljs-number">0</span>, index);<br> System.arraycopy(es, index, newElements, index + <span class="hljs-number">1</span>,<br> numMoved);<br> }<br> newElements[index] = element;<br> setArray(newElements);<br> }<br>}<br><span class="hljs-keyword">final</span> Object[] getArray() {<br> <span class="hljs-keyword">return</span> array;<br>}<br><span class="hljs-function"><span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setArray</span><span class="hljs-params">(Object[] a)</span> </span>{<br> array = a;<br>}<br></code></pre></td></tr></table></figure><blockquote><p>可以看到每次add的时候都会重新复制数组出来, 这样就会导致性能非常差</p></blockquote><h4 id="获取"><a href="#获取" class="headerlink" title="获取"></a>获取</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 简单的获取, 因为没有设计到数据更新</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-keyword">return</span> elementAt(getArray(), index);<br>}<br><span class="hljs-keyword">static</span> <E> <span class="hljs-function">E <span class="hljs-title">elementAt</span><span class="hljs-params">(Object[] a, <span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-keyword">return</span> (E) a[index];<br>}<br></code></pre></td></tr></table></figure><h4 id="迭代器"><a href="#迭代器" class="headerlink" title="迭代器"></a>迭代器</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> ListIterator<E> <span class="hljs-title">listIterator</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-comment">// 不支持写操作的迭代器</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> COWIterator<E>(getArray(), <span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure><blockquote><p><strong>什么是COW?</strong></p><p>Copy-On-Write简称COW, 是一种优化策略, 思路是所有人共享一个内容, 当某个人要修改时, 将这个数据复制一份进行修改, </p></blockquote><blockquote><p><strong>可以保证数据一致性吗?</strong> </p><p>COW无法保证数据实时一致性, 但是可以保证最终一致性. 会导致A线程拿到数据后, B线程修改了数据, 但是A线程拿着原有的数据进行了计算处理.</p></blockquote><h3 id="COW容器的缺点"><a href="#COW容器的缺点" class="headerlink" title="COW容器的缺点"></a>COW容器的缺点</h3><ol><li>内存占用问题, 因为特殊的写时复制机制导致写操作时, 会导致内存中同时有两个对象的内存.</li><li>数据一致性问题, 只能保证最终一致性, 无法保证实时一致性.</li></ol><p>因为各种实现机制的问题, 可以尽量处理读多写少的场景.</p>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>数据结构</tag>
<tag>List</tag>
<tag>多线程</tag>
</tags>
</entry>
<entry>
<title>LinkedList</title>
<link href="/2021/06/24/LinkedList/"/>
<url>/2021/06/24/LinkedList/</url>
<content type="html"><![CDATA[<h3 id="LinkedList"><a href="#LinkedList" class="headerlink" title="LinkedList"></a>LinkedList</h3><p>与ArrayList差不多, 只不过LinkedList基于链表实现.所以在插入和删除方面效率优于ArrayList, 但是随机访问ArrayList会更好.</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><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinkedList</span><<span class="hljs-title">E</span>></span><br><span class="hljs-class"> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractSequentialList</span><<span class="hljs-title">E</span>></span><br><span class="hljs-class"> // 双端队列 支持<span class="hljs-title">clone</span>支持序列化</span><br><span class="hljs-class"> <span class="hljs-keyword">implements</span> <span class="hljs-title">List</span><<span class="hljs-title">E</span>>, <span class="hljs-title">Deque</span><<span class="hljs-title">E</span>>, <span class="hljs-title">Cloneable</span>, <span class="hljs-title">java</span>.<span class="hljs-title">io</span>.<span class="hljs-title">Serializable</span></span><br><span class="hljs-class"></span>{}<br></code></pre></td></tr></table></figure><h4 id="diagram"><a href="#diagram" class="headerlink" title="diagram"></a>diagram</h4><p><img src="/imgs/LinkedList_diagram.jpg"></p><p>LinkedList可以被当作堆栈 (先进后出) 或者队列 (先进先出) 或者双端队列.</p><blockquote><p><strong>为什么添加删除效率高, 访问效率低?</strong></p><p>因为只需要改变指针指向, 访问效率低则是要对链表遍历.</p></blockquote><h3 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h3><h4 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 等价于addLast</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">add</span><span class="hljs-params">(E e)</span> </span>{<br> linkLast(e);<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br>}<br><span class="hljs-comment">// 将元素添加至链表的首位</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addFirst</span><span class="hljs-params">(E e)</span> </span>{<br> linkFirst(e);<br>}<br><span class="hljs-comment">// 将元素添加至链表的末位</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addLast</span><span class="hljs-params">(E e)</span> </span>{<br> linkLast(e);<br>}<br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">linkFirst</span><span class="hljs-params">(E e)</span> </span>{<br> <span class="hljs-keyword">final</span> Node<E> f = first;<br> <span class="hljs-comment">// 初始化对应元素, 因为向首位添加, 所以prev参数为null</span><br> <span class="hljs-keyword">final</span> Node<E> newNode = <span class="hljs-keyword">new</span> Node<>(<span class="hljs-keyword">null</span>, e, f);<br> <span class="hljs-comment">// 将新元素置为first(首位元素)</span><br> first = newNode;<br> <span class="hljs-keyword">if</span> (f == <span class="hljs-keyword">null</span>)<br> <span class="hljs-comment">// 没有末位元素时, 将last置为newNode</span><br> last = newNode;<br> <span class="hljs-keyword">else</span><br> <span class="hljs-comment">// 有末位元素时, 将f的上位元素置为newNode</span><br> f.prev = newNode;<br> size++;<br> modCount++;<br>}<br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">linkLast</span><span class="hljs-params">(E e)</span> </span>{<br> <span class="hljs-keyword">final</span> Node<E> l = last;<br> <span class="hljs-comment">// 初始化对应元素, 因为向末位添加, 所以next参数为null</span><br> <span class="hljs-keyword">final</span> Node<E> newNode = <span class="hljs-keyword">new</span> Node<>(l, e, <span class="hljs-keyword">null</span>);<br> last = newNode;<br> <span class="hljs-keyword">if</span> (l == <span class="hljs-keyword">null</span>)<br> <span class="hljs-comment">// 没有首位元素时, 将first置为newNode</span><br> first = newNode;<br> <span class="hljs-keyword">else</span><br> <span class="hljs-comment">// 没有首位元素时, 将l的下位元素置为newNode</span><br> l.next = newNode;<br> size++;<br> modCount++;<br>}<br><span class="hljs-comment">// 内部节点类</span><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span><<span class="hljs-title">E</span>> </span>{<br> E item;<br> <span class="hljs-comment">// 下一个元素</span><br> Node<E> next;<br> <span class="hljs-comment">// 上一个元素</span><br> Node<E> prev;<br><br> Node(Node<E> prev, E element, Node<E> next) {<br> <span class="hljs-keyword">this</span>.item = element;<br> <span class="hljs-keyword">this</span>.next = next;<br> <span class="hljs-keyword">this</span>.prev = prev;<br> }<br>}<br></code></pre></td></tr></table></figure><h4 id="判断包含"><a href="#判断包含" class="headerlink" title="判断包含."></a>判断包含.</h4><p>与ArrayList类似</p><h4 id="复制"><a href="#复制" class="headerlink" title="复制."></a>复制.</h4><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><code class="hljs java"><span class="hljs-comment">// 浅拷贝</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">clone</span><span class="hljs-params">()</span> </span>{<br> LinkedList<E> clone = superClone();<br> clone.first = clone.last = <span class="hljs-keyword">null</span>;<br> clone.size = <span class="hljs-number">0</span>;<br> clone.modCount = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (Node<E> x = first; x != <span class="hljs-keyword">null</span>; x = x.next)<br> clone.add(x.item);<br> <span class="hljs-keyword">return</span> clone;<br>}<br><span class="hljs-function"><span class="hljs-keyword">private</span> LinkedList<E> <span class="hljs-title">superClone</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">return</span> (LinkedList<E>) <span class="hljs-keyword">super</span>.clone();<br> } <span class="hljs-keyword">catch</span> (CloneNotSupportedException e) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InternalError(e);<br> }<br>}<br><span class="hljs-comment">// 与ArrayList类似</span><br><span class="hljs-keyword">public</span> Object[] toArray() {<br> <span class="hljs-comment">// ...</span><br>}<br><br></code></pre></td></tr></table></figure><h4 id="获取"><a href="#获取" class="headerlink" title="获取"></a>获取</h4><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> checkElementIndex(index);<br> <span class="hljs-keyword">return</span> node(index).item;<br>}<br><span class="hljs-comment">// 检查index</span><br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkElementIndex</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-keyword">if</span> (!isElementIndex(index))<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IndexOutOfBoundsException(outOfBoundsMsg(index));<br>}<br><span class="hljs-comment">// 确认index是否越界</span><br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isElementIndex</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-keyword">return</span> index >= <span class="hljs-number">0</span> && index < size;<br>}<br><span class="hljs-function">Node<E> <span class="hljs-title">node</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-comment">// 判断从首尾哪里获取快</span><br> <span class="hljs-keyword">if</span> (index < (size >> <span class="hljs-number">1</span>)) {<br> Node<E> x = first;<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < index; i++)<br> x = x.next;<br> <span class="hljs-keyword">return</span> x;<br> } <span class="hljs-keyword">else</span> {<br> Node<E> x = last;<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = size - <span class="hljs-number">1</span>; i > index; i--)<br> x = x.prev;<br> <span class="hljs-keyword">return</span> x;<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>从node方法中可以看出, 一定不要用fori的形式去遍历LinkedList, 使用foreach或者迭代器的形式遍历, fori的效率远低于迭代器</p></blockquote><h4 id="移除"><a href="#移除" class="headerlink" title="移除"></a>移除</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">remove</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-comment">// 验证下标</span><br> checkElementIndex(index);<br> <span class="hljs-comment">// 获取到对应的node元素, 传递到unlink中</span><br> <span class="hljs-keyword">return</span> unlink(node(index));<br>}<br><span class="hljs-function">E <span class="hljs-title">unlink</span><span class="hljs-params">(Node<E> x)</span> </span>{<br> <span class="hljs-keyword">final</span> E element = x.item;<br> <span class="hljs-keyword">final</span> Node<E> next = x.next;<br> <span class="hljs-keyword">final</span> Node<E> prev = x.prev;<br> <span class="hljs-comment">// 确认要移除元素是否为首位</span><br> <span class="hljs-keyword">if</span> (prev == <span class="hljs-keyword">null</span>) {<br> <span class="hljs-comment">// 是, 就把first修改为要移除元素的next, 即把第二位放到首位</span><br> first = next;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 不是, 就把上位的下位修改为当前的下位</span><br> prev.next = next;<br> x.prev = <span class="hljs-keyword">null</span>;<br> }<br> <span class="hljs-comment">// 确认要移除元素是否为末位</span><br> <span class="hljs-keyword">if</span> (next == <span class="hljs-keyword">null</span>) {<br> <span class="hljs-comment">// 是, 就把last修改为要移除元素的prev, 即把倒数第二位放到末位</span><br> last = prev;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 不是, 就把下位的上位修改为当前的上位</span><br> next.prev = prev;<br> x.next = <span class="hljs-keyword">null</span>;<br> }<br> x.item = <span class="hljs-keyword">null</span>;<br> size--;<br> modCount++;<br> <span class="hljs-comment">// 返回移除元素对象</span><br> <span class="hljs-keyword">return</span> element;<br>}<br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">remove</span><span class="hljs-params">(Object o)</span> </span>{<br><span class="hljs-comment">// 与ArrayList类似, 内层调用unlink</span><br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>数据结构</tag>
<tag>List</tag>
</tags>
</entry>
<entry>
<title>ArrayList</title>
<link href="/2021/06/23/ArrayList/"/>
<url>/2021/06/23/ArrayList/</url>
<content type="html"><![CDATA[<h3 id="ArrayList-关系"><a href="#ArrayList-关系" class="headerlink" title="ArrayList 关系"></a>ArrayList 关系</h3><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArrayList</span><<span class="hljs-title">E</span>> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractList</span><<span class="hljs-title">E</span>></span><br><span class="hljs-class"> // 支持随机访问支持克隆支持序列化</span><br><span class="hljs-class"> <span class="hljs-keyword">implements</span> <span class="hljs-title">List</span><<span class="hljs-title">E</span>>, <span class="hljs-title">RandomAccess</span>, <span class="hljs-title">Cloneable</span>, <span class="hljs-title">java</span>.<span class="hljs-title">io</span>.<span class="hljs-title">Serializable</span> </span>{<br>}<br></code></pre></td></tr></table></figure><h4 id="diagram"><a href="#diagram" class="headerlink" title="diagram"></a>diagram</h4><p><img src="/imgs/ArrayList_diagram.jpg"></p><p>初始化时, 会指向一个static空数组, 在第一次添加元素后, 将长度修改为10.</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 默认容量</span><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> DEFAULT_CAPACITY = <span class="hljs-number">10</span>;<br><span class="hljs-comment">// 初始默认空数组</span><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};<br><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ArrayList</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">this</span>.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;<br>}<br><br><span class="hljs-comment">// 添加元素时, 依次往下</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">add</span><span class="hljs-params">(E e)</span> </span>{<br> <span class="hljs-comment">// ...</span><br> add(e, elementData, size);<br> <span class="hljs-comment">// ...</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(E e, Object[] elementData, <span class="hljs-keyword">int</span> s)</span> </span>{<br> <span class="hljs-keyword">if</span> (s == elementData.length)<br> elementData = grow();<br> <span class="hljs-comment">// ...</span><br>}<br><br><span class="hljs-keyword">private</span> Object[] grow() {<br> <span class="hljs-keyword">return</span> grow(size + <span class="hljs-number">1</span>);<br>}<br><br><span class="hljs-keyword">private</span> Object[] grow(<span class="hljs-keyword">int</span> minCapacity) {<br> <span class="hljs-comment">// 通过copyOf复制出新的数组</span><br> <span class="hljs-keyword">return</span> elementData = Arrays.copyOf(elementData,<br> <span class="hljs-comment">// 产生新的容量</span><br> newCapacity(minCapacity));<br>}<br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">newCapacity</span><span class="hljs-params">(<span class="hljs-keyword">int</span> minCapacity)</span> </span>{<br> <span class="hljs-keyword">int</span> oldCapacity = elementData.length;<br> <span class="hljs-keyword">int</span> newCapacity = oldCapacity + (oldCapacity >> <span class="hljs-number">1</span>);<br> <span class="hljs-keyword">if</span> (newCapacity - minCapacity <= <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">if</span> (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)<br> <span class="hljs-keyword">return</span> Math.max(DEFAULT_CAPACITY, minCapacity);<br> <span class="hljs-comment">// ...</span><br> }<br> <span class="hljs-comment">// ...</span><br>}<br></code></pre></td></tr></table></figure><p>每次扩容都是增加到原来的1.5倍. 扩容的关键方法是<code>grow()</code>, 方法中是这么计算的.</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-comment">// Capacity: 容量; 这里的右移一位相当于除以2, 也就是说当oldCapacity = 10时, 最终计算结果为10 + 5 = 15</span><br>int <span class="hljs-keyword">new</span><span class="hljs-type">Capacity</span> = oldCapacity + (oldCapacity >> <span class="hljs-number">1</span>);<br></code></pre></td></tr></table></figure><p>由于扩容的方法本质是使用<code>Arrays.copyOf()</code>把源数组直接复制到新的数组中, 所以最好可以创建对象时, 指定大致的容量大小, 尽量去减少扩容的次数.</p><h3 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h3><h4 id="判空"><a href="#判空" class="headerlink" title="判空."></a>判空.</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><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><code class="hljs Java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">size</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">return</span> size;<br>}<br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isEmpty</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">return</span> size == <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><h4 id="判断包含"><a href="#判断包含" class="headerlink" title="判断包含."></a>判断包含.</h4><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></pre></td><td class="code"><pre><code class="hljs Java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">contains</span><span class="hljs-params">(Object o)</span> </span>{<br> <span class="hljs-keyword">return</span> indexOf(o) >= <span class="hljs-number">0</span>;<br>}<br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">indexOf</span><span class="hljs-params">(Object o)</span> </span>{<br> <span class="hljs-keyword">return</span> indexOfRange(o, <span class="hljs-number">0</span>, size);<br>}<br><span class="hljs-comment">// 将第一个匹配上的下标返回, 没有则为 -1</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">indexOfRange</span><span class="hljs-params">(Object o, <span class="hljs-keyword">int</span> start, <span class="hljs-keyword">int</span> end)</span> </span>{<br> Object[] es = elementData;<br> <span class="hljs-keyword">if</span> (o == <span class="hljs-keyword">null</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = start; i < end; i++) {<br> <span class="hljs-keyword">if</span> (es[i] == <span class="hljs-keyword">null</span>) {<br> <span class="hljs-keyword">return</span> i;<br> }<br> }<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = start; i < end; i++) {<br> <span class="hljs-keyword">if</span> (o.equals(es[i])) {<br> <span class="hljs-keyword">return</span> i;<br> }<br> }<br> }<br> <span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;<br>}<br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">lastIndexOf</span><span class="hljs-params">(Object o)</span> </span>{<br> <span class="hljs-comment">// 和indexOf相反</span><br>}<br></code></pre></td></tr></table></figure><h4 id="复制"><a href="#复制" class="headerlink" title="复制."></a>复制.</h4><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">clone</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">// 可以看出, 这里是浅拷贝, 修改v会影响原对象</span><br> ArrayList<?> v = (ArrayList<?>) <span class="hljs-keyword">super</span>.clone();<br> <span class="hljs-comment">// 然后又进行了copyOf, 对原有的数组进行拷贝, 这样就不会影响到原对象</span><br> v.elementData = Arrays.copyOf(elementData, size);<br> v.modCount = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">return</span> v;<br> } <span class="hljs-keyword">catch</span> (CloneNotSupportedException e) {<br> <span class="hljs-comment">// this shouldn't happen, since we are Cloneable</span><br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InternalError(e);<br> }<br>}<br><span class="hljs-comment">// 按照正确的顺序返回所有元素</span><br><span class="hljs-keyword">public</span> Object[] toArray() {<br> <span class="hljs-keyword">return</span> Arrays.copyOf(elementData, size);<br>}<br><span class="hljs-comment">// 将原对象复制到a中, 如果a的长度超出了size, 那么多余的均为null</span><br><span class="hljs-comment">// 原先a中有数据的话, 超出的部分不会变化, 但是在index小于size的均会被原数据覆盖</span><br><span class="hljs-keyword">public</span> <T> T[] toArray(T[] a) {<br> <span class="hljs-keyword">if</span> (a.length < size)<br> <span class="hljs-keyword">return</span> (T[]) Arrays.copyOf(elementData, size, a.getClass());<br> System.arraycopy(elementData, <span class="hljs-number">0</span>, a, <span class="hljs-number">0</span>, size);<br> <span class="hljs-keyword">if</span> (a.length > size)<br> a[size] = <span class="hljs-keyword">null</span>;<br> <span class="hljs-keyword">return</span> a;<br>}<br></code></pre></td></tr></table></figure><h4 id="获取"><a href="#获取" class="headerlink" title="获取."></a>获取.</h4><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><code class="hljs java"><span class="hljs-comment">// 根据下标获取对应元素</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-comment">// 验证index是否越界</span><br> Objects.checkIndex(index, size);<br> <span class="hljs-comment">// 根据对应下标拿到对应元素</span><br> <span class="hljs-keyword">return</span> elementData(index);<br>}<br><span class="hljs-function">E <span class="hljs-title">elementData</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-keyword">return</span> (E) elementData[index];<br>}<br></code></pre></td></tr></table></figure><h4 id="覆盖"><a href="#覆盖" class="headerlink" title="覆盖"></a>覆盖</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 用指定的元素替换指定位置的元素, 返回老元素</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">set</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index, E element)</span> </span>{<br> <span class="hljs-comment">// 验证index是否越界</span><br> Objects.checkIndex(index, size);<br> <span class="hljs-comment">// 根据对应下标拿到对应元素</span><br> E oldValue = elementData(index);<br> <span class="hljs-comment">// 覆盖新元素</span><br> elementData[index] = element;<br> <span class="hljs-comment">// 返回老元素</span><br> <span class="hljs-keyword">return</span> oldValue;<br>}<br></code></pre></td></tr></table></figure><h4 id="移除"><a href="#移除" class="headerlink" title="移除"></a>移除</h4><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></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">remove</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> <span class="hljs-comment">// 验证index是否越界</span><br> rangeCheck(index);<br> modCount++;<br> E oldValue = elementData(index);<br> <span class="hljs-comment">// 计算要移除的元素下标</span><br> <span class="hljs-keyword">int</span> numMoved = size - index - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (numMoved > <span class="hljs-number">0</span>)<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * elementData 源数组</span><br><span class="hljs-comment"> * index+1 源数组中开始拷贝的索引值</span><br><span class="hljs-comment"> * elementData 目标数组</span><br><span class="hljs-comment"> * index 拷贝到目标数组开始的索引值</span><br><span class="hljs-comment"> * numMoved 拷贝元素的个数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-comment">// 拷贝数组到源数组, 即覆盖</span><br> System.arraycopy(elementData, index+<span class="hljs-number">1</span>, elementData, index,<br> numMoved);<br> elementData[--size] = <span class="hljs-keyword">null</span>;<br> <span class="hljs-keyword">return</span> oldValue;<br>}<br><span class="hljs-comment">// 类似于indexOf</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">remove</span><span class="hljs-params">(Object o)</span> </span>{<br> <span class="hljs-keyword">if</span> (o == <span class="hljs-keyword">null</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> index = <span class="hljs-number">0</span>; index < size; index++)<br> <span class="hljs-keyword">if</span> (elementData[index] == <span class="hljs-keyword">null</span>) {<br> fastRemove(index);<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br> }<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> index = <span class="hljs-number">0</span>; index < size; index++)<br> <span class="hljs-keyword">if</span> (o.equals(elementData[index])) {<br> fastRemove(index);<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;<br>}<br><span class="hljs-comment">// 快速移除, 相对于remove少了校验index和返回值</span><br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">fastRemove</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{<br> modCount++;<br> <span class="hljs-keyword">int</span> numMoved = size - index - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (numMoved > <span class="hljs-number">0</span>)<br> System.arraycopy(elementData, index+<span class="hljs-number">1</span>, elementData, index,<br> numMoved);<br> elementData[--size] = <span class="hljs-keyword">null</span>;<br>}<br><span class="hljs-comment">// 将所有的元素都置为null, size重新置为0</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clear</span><span class="hljs-params">()</span> </span>{<br> modCount++;<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < size; i++)<br> elementData[i] = <span class="hljs-keyword">null</span>;<br> size = <span class="hljs-number">0</span>;<br>}<br><span class="hljs-comment">// 移除所有在c中存在的元素, 如果源数组被修改则返回true</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">removeAll</span><span class="hljs-params">(Collection<?> c)</span> </span>{<br> Objects.requireNonNull(c);<br> <span class="hljs-keyword">return</span> batchRemove(c, <span class="hljs-keyword">false</span>);<br>}<br><span class="hljs-comment">// 保留所有在c中存在的元素, 如果源数组被修改则返回true</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">retainAll</span><span class="hljs-params">(Collection<?> c)</span> </span>{<br> Objects.requireNonNull(c);<br> <span class="hljs-keyword">return</span> batchRemove(c, <span class="hljs-keyword">true</span>);<br>}<br><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">batchRemove</span><span class="hljs-params">(Collection<?> c, <span class="hljs-keyword">boolean</span> complement)</span> </span>{<br> <span class="hljs-keyword">final</span> Object[] elementData = <span class="hljs-keyword">this</span>.elementData;<br> <span class="hljs-keyword">int</span> r = <span class="hljs-number">0</span>, w = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">boolean</span> modified = <span class="hljs-keyword">false</span>;<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">for</span> (; r < size; r++)<br> <span class="hljs-comment">// 为true时, 将对应的元素放到最前边</span><br> <span class="hljs-keyword">if</span> (c.contains(elementData[r]) == complement)<br> elementData[w++] = elementData[r];<br> } <span class="hljs-keyword">finally</span> {<br> <span class="hljs-comment">// 如果抛出异常, 那么保存未处理完成的数组</span><br> <span class="hljs-keyword">if</span> (r != size) {<br> System.arraycopy(elementData, r,<br> elementData, w,<br> size - r);<br> w += size - r;<br> }<br> <span class="hljs-comment">// w == size时, 本质就是不需要变动, 当不等于size时, 将在元素下标大于w的全部置为null, 等待GC回收</span><br> <span class="hljs-keyword">if</span> (w != size) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = w; i < size; i++)<br> elementData[i] = <span class="hljs-keyword">null</span>;<br> modCount += size - w;<br> size = w;<br> modified = <span class="hljs-keyword">true</span>;<br> }<br> }<br> <span class="hljs-keyword">return</span> modified;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>数据结构</tag>
<tag>List</tag>
</tags>
</entry>
<entry>
<title>线性与非线性</title>
<link href="/2021/06/23/%E7%BA%BF%E6%80%A7%E4%B8%8E%E9%9D%9E%E7%BA%BF%E6%80%A7/"/>
<url>/2021/06/23/%E7%BA%BF%E6%80%A7%E4%B8%8E%E9%9D%9E%E7%BA%BF%E6%80%A7/</url>
<content type="html"><![CDATA[<h3 id="线性结构"><a href="#线性结构" class="headerlink" title="线性结构"></a>线性结构</h3><p>有序数据元素的集合. </p><p>数据元素之间的关系是一对一的关系, 除了第一个和最后一个数据元素之外, 其它数据元素都是首尾相接的.</p><h3 id="非线性结构"><a href="#非线性结构" class="headerlink" title="非线性结构"></a>非线性结构</h3><p>每个数据元素可能与零个或者多个其他数据元素产生关系. 根据关系的不同, 可分为层次结构和群结构.</p>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>数据结构</tag>
</tags>
</entry>
<entry>
<title>线程池</title>
<link href="/2021/06/22/%E7%BA%BF%E7%A8%8B%E6%B1%A0/"/>
<url>/2021/06/22/%E7%BA%BF%E7%A8%8B%E6%B1%A0/</url>
<content type="html"><![CDATA[<blockquote><p><strong>自己该怎么设计线程池?</strong></p><p>首先池化技术就是预先保存好资源, 以后续使用; 那么线程池的实现就是创建大量的线程进行统一管理.</p></blockquote><p>线程池(Thread Pool)是一种基于池化思想管理线程的<strong>工具</strong>.</p><blockquote><p> <strong>为什么需要池化技术</strong></p><p>现在要去数据库查询十次数据, 那么就需要连接十次数据库, 导致连接的时间成为整个等待时间中占比最高的, 所以有了池化技术, 每次走的时候只需要拿到连接, 执行查询即可, 可以节省出来创建,销毁等等.</p></blockquote><h3 id="使用线程池的好处"><a href="#使用线程池的好处" class="headerlink" title="使用线程池的好处"></a>使用线程池的好处</h3><ul><li>无需等待创建, 直接<strong>复用已创建的线程</strong>, 侧面<strong>降低资源消耗,等待时间</strong>.</li><li>对线程进行<strong>统一的管理</strong>.</li><li>可进行更多的<strong>拓展</strong>. (延期等)</li></ul><h1 id="未完待续…"><a href="#未完待续…" class="headerlink" title="未完待续…"></a>未完待续…</h1>]]></content>
<categories>
<category>Java</category>
</categories>
</entry>
</search>