-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
813 lines (528 loc) · 60 KB
/
index.html
File metadata and controls
813 lines (528 loc) · 60 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Code Work Life | 思考很重要,行动更重要</title>
<meta name="author" content="wanglei">
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta property="og:site_name" content="Code Work Life"/>
<meta property="og:image" content="/favicon.ico"/>
<link href="/favicon.png" rel="icon">
<link rel="alternate" href="/atom.xml" title="Code Work Life" type="application/atom+xml">
<link rel="stylesheet" href="/css/style.css" media="screen" type="text/css">
</head>
<body>
<div class="blog">
<div class="content">
<header>
<div class="site-branding">
<h1 class="site-title">
<a href="/">Code Work Life</a>
</h1>
<p class="site-description">思考很重要,行动更重要</p>
</div>
<nav class="site-navigation">
<ul>
<li><a href="/">主页</a></li>
<li><a href="/archives">归档</a></li>
<li><a href="/about">关于</a></li>
</ul>
</nav>
</header>
<main class="site-main posts-loop">
<article>
<h3 class="article-title"><a href="/2020/01/01/【MySql】摘要/"><span>【MySql】摘要</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2020/01/01/【MySql】摘要/" rel="bookmark">
<time class="entry-date published" datetime="2020-01-01T08:54:34.000Z">
2020-01-01
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">原文 https://www.aneasystone.com/archives/2018/04/solving-dead-locks-four.html</span><br></pre></td></tr></table></figure>
<h3 id="摘要"><a href="#摘要" class="headerlink" title="摘要"></a>摘要</h3><ul>
<li><p>要知道在范围查询时,加锁是一条记录一条记录挨个加锁的,所以虽然只有一条 SQL 语句,如果两条 SQL 语句的加锁顺序不一样,也会导致死锁。</p>
</li>
<li><p>日志锁类型</p>
<ol>
<li>记录锁(<strong>LOCK_REC_NOT_GAP</strong>): lock_mode X locks rec but not gap</li>
<li>间隙锁(<strong>LOCK_GAP</strong>): lock_mode X locks gap before rec</li>
<li>Next-key 锁(<strong>LOCK_ORNIDARY</strong>): lock_mode X</li>
<li>插入意向锁(<strong>LOCK_INSERT_INTENTION</strong>): lock_mode X locks gap before rec insert intention</li>
</ol>
</li>
</ul>
<p>这里有一点要注意的是,并不是在日志里看到 lock_mode X 就认为这是 Next-key 锁,因为还有一个例外:如果在 <strong><em>supremum record</em></strong> 上加锁,<em>locks gap before rec</em> 会省略掉,间隙锁会显示成 lock_mode X,插入意向锁会显示成 <em>lock_mode X insert intention</em>。譬如下面这个<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">RECORD LOCKS space id 0 page no 307 n bits 72 index `PRIMARY` of table `test`.`test` trx id 50F lock_mode X </span><br><span class="line">Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0</span><br></pre></td></tr></table></figure></p>
<ul>
<li>只有访问到的对象才会加锁,这个查询本身是有覆盖索引,并不需要访问主键,因此id主键上并没有锁</li>
</ul>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2019/02/25/mysql中的行级锁/"><span>mysql中的行级锁</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2019/02/25/mysql中的行级锁/" rel="bookmark">
<time class="entry-date published" datetime="2019-02-25T09:28:37.000Z">
2019-02-25
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h2 id="gap锁和next-key的意义"><a href="#gap锁和next-key的意义" class="headerlink" title="gap锁和next-key的意义"></a>gap锁和next-key的意义</h2><p>读过一些关于mysql的博文,都没有搞清楚什么是gap锁,什么next-key锁。直到看到一些官方文档,才对这些锁有了更清楚的认识。<a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html" target="_blank" rel="noopener">官方直通门</a></p>
<h3 id="gap-locks"><a href="#gap-locks" class="headerlink" title="gap locks"></a>gap locks</h3><p>引用官方文档</p>
<blockquote>
<p>A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record</p>
</blockquote>
<p>这句是一个定义,我们能了解到,gap锁是作用在 index records即索引数据上。但对于实际应用还不是很透彻,但后面的文档举了一个例子</p>
<blockquote>
<p>For example, SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; prevents other transactions from inserting a value of 15 into column t.c1, whether or not there was already any such value in the column, because the gaps between all existing values in the range are locked.</p>
</blockquote>
<p>比较重要的一部分是 <strong><em>prevents other transactions from inserting a value of 15 into column t.c1</em></strong></p>
<p>其中<strong><em>inserting</em></strong> 这个动词,表明了gap锁的意义。gap locks是为了阻止其他事务的insert操作。</p>
<h3 id="next-key-locks"><a href="#next-key-locks" class="headerlink" title="next-key locks"></a>next-key locks</h3><p>同样引用官方文档</p>
<blockquote>
<p>A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.</p>
</blockquote>
<p>通过上面的文档,我们能理解到,next-key locks实际上是两种锁的组合。表明它既有record lock的特性,也有 gap lock 的特性。<br>那它到底是如何起作用的呢?</p>
<blockquote>
<p>A next-key lock on an index record also affects the “gap” before that index record</p>
</blockquote>
<blockquote>
<p>that is, a next-key lock is an index-record lock plus a gap lock on the gap preceding the index record.</p>
</blockquote>
<p>首先 next-key是针对索引数据的key,其次,它还对该索引记录的之前的数据有 gap locks的作用。</p>
<p>怎么理解呢?官方文档是这么说的</p>
<blockquote>
<p>If one session has a shared or exclusive lock on record R in an index, another session cannot insert a new index record in the gap immediately before R in the index order.</p>
</blockquote>
<p>举个🌰,假设有表child,其表结构为:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `child` (</span><br><span class="line"> `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line"> `name` varchar(22) DEFAULT NULL,</span><br><span class="line"> `age` int(11) DEFAULT NULL,</span><br><span class="line"> PRIMARY KEY (`id`),</span><br><span class="line"> KEY `age` (`age`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT DEFAULT CHARSET=utf8mb4;</span><br></pre></td></tr></table></figure></p>
<p>假设有如下数据</p>
<table>
<thead>
<tr>
<th style="text-align:left">id</th>
<th style="text-align:center">name</th>
<th style="text-align:right">age</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">1</td>
<td style="text-align:center">jack</td>
<td style="text-align:right">23</td>
</tr>
<tr>
<td style="text-align:left">2</td>
<td style="text-align:center">rose</td>
<td style="text-align:right">19</td>
</tr>
</tbody>
</table>
<p>我们age列上是有普通索引的。那么根据next-key locks的定义,其实我们会有如下的 next-key locks锁</p>
<table>
<thead>
<tr>
<th style="text-align:left">next-key locks</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">(negative infinity, 19]</td>
</tr>
<tr>
<td style="text-align:left">(19, 23]</td>
</tr>
<tr>
<td style="text-align:left">(23, positive infinity)</td>
</tr>
</tbody>
</table>
<p>所以,当有两个事务A、B同时执行,但A执行语句在B之前。<br>假设A为<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">update child set name="Lucy" where age=19;</span><br></pre></td></tr></table></figure></p>
<p>B为</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">insert child values(null,"Lily",8);</span><br></pre></td></tr></table></figure>
<p>那么根据定义,mysql会锁住 (negative infinity, 19]这块的数据,那么就会导致B事务堵塞。因为 8 在 (negative infinity, 19]中。但是如果是<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">insert child values(null,"Lily",23);</span><br></pre></td></tr></table></figure></p>
<p>就可以。</p>
<p>我们再看下官方文档</p>
<blockquote>
<p>InnoDB uses next-key locks for searches and index scans, which prevents phantom rows</p>
</blockquote>
<p>通过上面我们能理解到next-key实际是为了解决<strong><em>幻读</em></strong>问题,<br>那么实际依然是阻止了其他事务的<strong><em>insert</em></strong>操作。</p>
<p><strong><em>大家注意,以上例子,都是在普通索引上的操作,如果你操作的列是有unique索引或者是主键索引并且锁定的数据是表有的,那么都不会有gap locks的特性,及退化为行锁</em></strong><br><strong><em>若执行的条件作用在唯一索引或者主键索引时,该条件实际不存在表里,那么依然会锁住该条件的左右两个区间</em></strong></p>
<blockquote>
<p>Gap locking is not needed for statements that lock rows using a unique index to search for a unique row. (This does not include the case that the search condition includes only some columns of a multiple-column unique index; in that case, gap locking does occur.) </p>
</blockquote>
<p>所以,合适索引,不仅能保持查询效率,也可以保证行级别的精确性。</p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/mysql/">mysql</a><a href="/tags/innodb/">innodb</a><a href="/tags/官方文档/">官方文档</a><a href="/tags/gap-locks/">gap locks</a><a href="/tags/next-key-locks/">next-key locks</a><a href="/tags/mysql-5-7/">mysql 5.7</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/11/20/关于mysql表情符号存储简析/"><span>关于mysql表情符号存储简析</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/11/20/关于mysql表情符号存储简析/" rel="bookmark">
<time class="entry-date published" datetime="2018-11-20T08:28:47.000Z">
2018-11-20
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h2 id="关键点的分析"><a href="#关键点的分析" class="headerlink" title="关键点的分析"></a>关键点的分析</h2><p>关于mysql存储表情符号的教程,网上一大堆。但根据一些教程设置后,还是会出现乱码的现象。根本的原因在于,没有了解整个存储过程。</p>
<p>解决表情符号问题要从以下几点关注:</p>
<ul>
<li>clinet端</li>
<li>connection链接(也可以理解为运输部分)</li>
<li>table存储</li>
<li>query查询(也可以理解为result结果)</li>
</ul>
<p>简言之,首先你和msyql进行交互时,你的client端首先需要编码正确,然后你将编码正确的数据必须在能够运输正确的轨道上运输(connection),最后要能把数据放在能够正确存储编码的表里。至此已经完了存储。接下来实际时读取,也是query过程,这个过程也要保证connection正确,然后就是返回的结果数据也要正确编码。</p>
<p>因此我在项目只设置了两项。</p>
<ol>
<li>table表属性为 uft8mb4</li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/charset-connection.html" target="_blank" rel="noopener">set names utf8mb4</a> </li>
</ol>
<p>第二项等同于 <strong><em>character_set_client</em></strong>, <strong><em>character_set_results</em></strong>, <strong><em>character_set_connection</em></strong> 设置了这三项的值。</p>
<p>还有注意一点,就是mysql的一些属性的作用域只在当前会话中。因此,在实际使用过程中,需要在每次获取链接时,进行设置。有些三方的数据库链接池是有connectionInitSqls等属性支持的。</p>
<p>其实,任意编码问题都可以从这个方向进行梳理,数据元格式必须正确,传输过程必须支持,存储需要支持,读取也要按正确的编码进行读取。</p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/mysql/">mysql</a><a href="/tags/表情符号/">表情符号</a><a href="/tags/emoji/">emoji</a><a href="/tags/编码问题/">编码问题</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/11/19/生产环境canal发生oom异常简记/"><span>生产环境canal发生oom异常简记</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/11/19/生产环境canal发生oom异常简记/" rel="bookmark">
<time class="entry-date published" datetime="2018-11-19T09:50:54.000Z">
2018-11-19
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p><a href="https://github.com/alibaba/canal" target="_blank" rel="noopener">canal</a>是alibaba开源一个基于binlog的增量订阅&消费组件。以一个生产者消费者的经典模式,将生产者canal-server伪装成一个mysql的从库,对binlog进行解读,消费者通过轮询方式,获取binlog的数据操作内容。利用canal可以实现很多有意思的场景。</p>
<p>我们在生产环境中,利用canal+kafka的架构,来实现一下报表等功能。</p>
<h2 id="OOM问题"><a href="#OOM问题" class="headerlink" title="OOM问题"></a>OOM问题</h2><p>一开始在使用canal并没有特别大的问题,配置也利用了默认配置(<strong>多了解配置,可以帮助你更清楚的搞懂这个工具的原理</strong>)。直到有次,线上库进行一次数据批量导入,canal的消费者异常邮件狂爆,即使备份(canal有个基于zk的冷备机制)起来了,不一会备份也会爆邮件。</p>
<p>查看日志,发现OOM了,canal-server在启动脚本增加了dump的命令(还是阿里大法厉害)。通过dump日志我们发现了问题原因。由于批量倒库,一条<code>insert values(xxx)</code>有1M的大小。<br>canal-server将消费的binlog会存储在内存中,并利用Ringbuffer一种数据结构进行存储,这种数据结构比较有意思,在很多场景中都有用到过,最著名的是<a href="http://ifeve.com/disruptor" target="_blank" rel="noopener">disruptor 中文版</a>。</p>
<p>canal-server在设置Ringbuffer的默认大小是16384,16384*1M约等于16G,内存根本吃不消,导致OOM的发生,之后调大了heap,并将Ringbuffer的大小调小到1024。</p>
<p>线上就再也没有发生过OOM。</p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/java/">java</a><a href="/tags/canal/">canal</a><a href="/tags/OOM/">OOM</a><a href="/tags/RingBuffer/">RingBuffer</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/08/30/HttpClient针对IPV6的支持/"><span>HttpClient针对IPV6的支持</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/08/30/HttpClient针对IPV6的支持/" rel="bookmark">
<time class="entry-date published" datetime="2018-08-30T10:13:37.000Z">
2018-08-30
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<p>ipv6会逐渐代替ipv4协议,对于一些插件使用,若不及时更新版本,会有意想不到的问题。比如java中常用的httplcient组件。</p>
<h2 id="HttpClient-版本不能低于4-3-6"><a href="#HttpClient-版本不能低于4-3-6" class="headerlink" title="HttpClient 版本不能低于4.3.6"></a>HttpClient 版本不能低于4.3.6</h2><p><strong>原因</strong>:在4.3.6中,httpclient新增了对于异常NoRouteToHostException的处理,低于4.3.6的httpclient会忽视这个异常,导致链接失败。</p>
<h2 id="NoRouteToHostException的异常处理"><a href="#NoRouteToHostException的异常处理" class="headerlink" title="NoRouteToHostException的异常处理"></a>NoRouteToHostException的异常处理</h2><h3 id="源码4-3-6"><a href="#源码4-3-6" class="headerlink" title="源码4.3.6"></a>源码4.3.6</h3><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">class HttpClientConnectionOperator {</span><br><span class="line"> ...省略其他代码</span><br><span class="line"> //关注此方法的异常处理</span><br><span class="line"> public void connect(ManagedHttpClientConnection conn, HttpHost host, InetSocketAddress localAddress, int connectTimeout, SocketConfig socketConfig, HttpContext context) throws IOException {</span><br><span class="line"> Lookup<ConnectionSocketFactory> registry = this.getSocketFactoryRegistry(context);</span><br><span class="line"> ConnectionSocketFactory sf = (ConnectionSocketFactory)registry.lookup(host.getSchemeName());</span><br><span class="line"> if (sf == null) {</span><br><span class="line"> throw new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported");</span><br><span class="line"> } else {</span><br><span class="line"> 若host包含ipv4和ipv6,则addresses会包含2个</span><br><span class="line"> InetAddress[] addresses = host.getAddress() != null ? new InetAddress[]{host.getAddress()} : this.dnsResolver.resolve(host.getHostName());</span><br><span class="line"> int port = this.schemePortResolver.resolve(host);</span><br><span class="line"></span><br><span class="line"> for(int i = 0; i < addresses.length; ++i) {</span><br><span class="line"> InetAddress address = addresses[i];</span><br><span class="line"> boolean last = i == addresses.length - 1;</span><br><span class="line"> Socket sock = sf.createSocket(context);</span><br><span class="line"> sock.setSoTimeout(socketConfig.getSoTimeout());</span><br><span class="line"> sock.setReuseAddress(socketConfig.isSoReuseAddress());</span><br><span class="line"> sock.setTcpNoDelay(socketConfig.isTcpNoDelay());</span><br><span class="line"> sock.setKeepAlive(socketConfig.isSoKeepAlive());</span><br><span class="line"> int linger = socketConfig.getSoLinger();</span><br><span class="line"> if (linger >= 0) {</span><br><span class="line"> sock.setSoLinger(linger > 0, linger);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> conn.bind(sock);</span><br><span class="line"> InetSocketAddress remoteAddress = new InetSocketAddress(address, port);</span><br><span class="line"> if (this.log.isDebugEnabled()) {</span><br><span class="line"> this.log.debug("Connecting to " + remoteAddress);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> try {</span><br><span class="line"> sock = sf.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);</span><br><span class="line"> conn.bind(sock);</span><br><span class="line"> if (this.log.isDebugEnabled()) {</span><br><span class="line"> this.log.debug("Connection established " + conn);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return;</span><br><span class="line"> } catch (SocketTimeoutException var19) {</span><br><span class="line"> if (last) {</span><br><span class="line"> throw new ConnectTimeoutException(var19, host, addresses);</span><br><span class="line"> }</span><br><span class="line"> } catch (ConnectException var20) {</span><br><span class="line"> if (last) {</span><br><span class="line"> String msg = var20.getMessage();</span><br><span class="line"> if ("Connection timed out".equals(msg)) {</span><br><span class="line"> throw new ConnectTimeoutException(var20, host, addresses);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> throw new HttpHostConnectException(var20, host, addresses);</span><br><span class="line"> }</span><br><span class="line"> //此处新增了对于NoRouteToHostException的处理,若访问的host同时拥有ipv4和ipv6,但链接不支持ipv6,会抛出此异常。若不加以处理,则直接会直接报错,退出。</span><br><span class="line"> } catch (NoRouteToHostException var21) {</span><br><span class="line"> if (last) {</span><br><span class="line"> throw var21;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (this.log.isDebugEnabled()) {</span><br><span class="line"> this.log.debug("Connect to " + remoteAddress + " timed out. " + "Connection will be retried using another IP address");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="源码4-3-5"><a href="#源码4-3-5" class="headerlink" title="源码4.3.5"></a>源码4.3.5</h3><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">class HttpClientConnectionOperator {</span><br><span class="line"> ...省略其他代码</span><br><span class="line"> public void connect(</span><br><span class="line"> final ManagedHttpClientConnection conn,</span><br><span class="line"> final HttpHost host,</span><br><span class="line"> final InetSocketAddress localAddress,</span><br><span class="line"> final int connectTimeout,</span><br><span class="line"> final SocketConfig socketConfig,</span><br><span class="line"> final HttpContext context) throws IOException {</span><br><span class="line"> final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);</span><br><span class="line"> final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());</span><br><span class="line"> if (sf == null) {</span><br><span class="line"> throw new UnsupportedSchemeException(host.getSchemeName() +</span><br><span class="line"> " protocol is not supported");</span><br><span class="line"> }</span><br><span class="line"> //若host包含ipv4和ipv6,则addresses会包含2个</span><br><span class="line"> final InetAddress[] addresses = this.dnsResolver.resolve(host.getHostName());</span><br><span class="line"> final int port = this.schemePortResolver.resolve(host);</span><br><span class="line"> for (int i = 0; i < addresses.length; i++) {</span><br><span class="line"> final InetAddress address = addresses[i];</span><br><span class="line"> final boolean last = i == addresses.length - 1;</span><br><span class="line"></span><br><span class="line"> Socket sock = sf.createSocket(context);</span><br><span class="line"> sock.setSoTimeout(socketConfig.getSoTimeout());</span><br><span class="line"> sock.setReuseAddress(socketConfig.isSoReuseAddress());</span><br><span class="line"> sock.setTcpNoDelay(socketConfig.isTcpNoDelay());</span><br><span class="line"> sock.setKeepAlive(socketConfig.isSoKeepAlive());</span><br><span class="line"> final int linger = socketConfig.getSoLinger();</span><br><span class="line"> if (linger >= 0) {</span><br><span class="line"> sock.setSoLinger(linger > 0, linger);</span><br><span class="line"> }</span><br><span class="line"> conn.bind(sock);</span><br><span class="line"></span><br><span class="line"> final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);</span><br><span class="line"> if (this.log.isDebugEnabled()) {</span><br><span class="line"> this.log.debug("Connecting to " + remoteAddress);</span><br><span class="line"> }</span><br><span class="line"> try {</span><br><span class="line"> sock = sf.connectSocket(</span><br><span class="line"> connectTimeout, sock, host, remoteAddress, localAddress, context);</span><br><span class="line"> conn.bind(sock);</span><br><span class="line"> if (this.log.isDebugEnabled()) {</span><br><span class="line"> this.log.debug("Connection established " + conn);</span><br><span class="line"> }</span><br><span class="line"> return;</span><br><span class="line"> } catch (final SocketTimeoutException ex) {</span><br><span class="line"> if (last) {</span><br><span class="line"> throw new ConnectTimeoutException(ex, host, addresses);</span><br><span class="line"> }</span><br><span class="line"> //相对于源码4.3.6 这里缺少了对于 NoRouteToHostException 异常处理,导致链接失败。</span><br><span class="line"> } catch (final ConnectException ex) {</span><br><span class="line"> if (last) {</span><br><span class="line"> final String msg = ex.getMessage();</span><br><span class="line"> if ("Connection timed out".equals(msg)) {</span><br><span class="line"> throw new ConnectTimeoutException(ex, host, addresses);</span><br><span class="line"> } else {</span><br><span class="line"> throw new HttpHostConnectException(ex, host, addresses);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (this.log.isDebugEnabled()) {</span><br><span class="line"> this.log.debug("Connect to " + remoteAddress + " timed out. " +</span><br><span class="line"> "Connection will be retried using another IP address");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong><em>概括一下:方法connect会对InetAddress[]进行一次循环,源码4.3.5在未对 NoRouteToHostException 进行处理,则方法会直接退出,并不会尝试第二个地址链接。</em></strong></p>
<h2 id="JVM对IPV4和IPV6的选择"><a href="#JVM对IPV4和IPV6的选择" class="headerlink" title="JVM对IPV4和IPV6的选择"></a>JVM对IPV4和IPV6的选择</h2><p><strong><em>java.net.preferIPv4Stack (default: false)</em></strong> If IPv6 is available on the operating system the underlying native socket will be, by default, an IPv6 socket which lets applications connect to, and accept connections from, both IPv4 and IPv6 hosts. However, in the case an application would rather use IPv4 only sockets, then this property can be set to true. The implication is that it will not be possible for the application to communicate with IPv6 only hosts.</p>
<p><strong><em>java.net.preferIPv6Addresses (default: false)</em></strong> When dealing with a host which has both IPv4 and IPv6 addresses, and if IPv6 is available on the operating system, the default behavior is to prefer using IPv4 addresses over IPv6 ones. This is to ensure backward compatibility, for example applications that depend on the representation of an IPv4 address (e.g. 192.168.1.1). This property can be set to true to change that preference and use IPv6 addresses over IPv4 ones where possible.</p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/ipv6/">ipv6</a><a href="/tags/httpclient/">httpclient</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/05/05/北野武的小酒馆/"><span>北野武的小酒馆</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/05/05/北野武的小酒馆/" rel="bookmark">
<time class="entry-date published" datetime="2018-05-05T04:12:46.000Z">
2018-05-05
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h1 id="小酒馆"><a href="#小酒馆" class="headerlink" title="小酒馆"></a>小酒馆</h1><p>期盼的书店终于开业了,在朋友圈充斥着“旅行的欢乐”时,我只能用读书来丰富“灵魂”(穷也没有其他办法)。</p>
<blockquote>
<p>要么读书,要么旅行,身体和灵魂,需要有一个在路上</p>
</blockquote>
<p>书店的座位需要付费。还是比较喜欢这样的设置。人少,安静。可以买杯咖啡,呆一下午。(早上10点才开门,只能吃完午饭再来,不然得有两次消费)</p>
<p>书店不仅卖书,还会卖点笔记本、小音箱、玻璃杯等小物品。挺中意一个笔记本,淘宝了一下,大约108大洋。嗯,在这里应该更贵吧。</p>
<p>买了杯拿铁入座,随手拿了本《北野武的小酒馆》。完全是冲书名去的,酒在以前觉得特别难喝,除了酒桌上用来拼的东西,平常一点都不会碰。但后来,开始工作后,开始有压力后,开始迷茫的时候,晚上喝点啤酒,吃点小菜。醉醺醺的感觉,才让我觉得为什么酒是这么的“好”。</p>
<blockquote>
<p>就这样,我下定决心要退学。<br>至少在那一刻,我对死的恐惧消失得无影无踪了。 《北野武的小酒馆》</p>
</blockquote>
<p>下定决心的那一刻,总是瞬间充满力量。<br>不开心的是,充满力量,总是那一刻。</p>
<p>北野武在青春期,总是会考虑“生死”的问题(我青春期时,除了叛逆就是叛逆^_^)。在家庭里,母亲的话分量及重,父亲是个软弱的工人,挣得钱也似乎只能够他下酒馆,家里一共3个男孩1个女孩,基本都是母亲一手养大并送入大学。母亲希望北野武能考入理工类大学,进入大企业(原来天下的家长一般黑)。</p>
<blockquote>
<p>从根本上说,如果我就这样走在母亲为我设计好的人生道路上,其结果也不一定是不幸呀。只不过,这样的话这世上就会少了一个叫北野武的艺人,只有这一点是明确无误的。不过,这是题外话了。《北野武的小酒馆》</p>
</blockquote>
<p>我觉得,爱情不再是世界上最奢侈的东西,“理想”才是。坚定理想的人,才是勇敢的人。</p>
<blockquote>
<p>认认真真、勤勤恳恳地工作,爱护家人,抚养小孩,即便只是做这点事,也能够充分获得人生的满足感。成了个名人呀,拍了部好电影呀,由此获得的满足感和一般的满足感其实没有多大区别,到了我这个年龄对此就知道得一清二楚了。<br>话虽这么说,但你这个家伙到底想选择哪种人生呢?<br>如果您这么问,我会这样回答:虽然辛苦,我还是会选择那种滚烫的人生。 《北野武的小酒馆》</p>
</blockquote>
<blockquote>
<p>即便是有机会让我的人生重新来过,我想我还是会选择那种会以几亿度的高温飞速燃烧的人生。</p>
</blockquote>
<p>从来不缺下定决心,勇气之类的东西。<br>缺得是,对于失败的承受能力。</p>
<blockquote>
<p>以前有这样一个傻瓜,他不抽烟、不喝酒,也不要女人,只是一个劲地干活,就这样活到了一百岁。 要我过这样的生活,还如让我去死。《北野武的小酒馆》</p>
</blockquote>
<p>潇洒,潇洒,人生真的只一次,努力去尝试自己喜欢的一切吧。</p>
<blockquote>
<p>人生是不平等的</p>
<p>不管你多努力,做不到的事情就是做不到《北野武的小酒馆》</p>
</blockquote>
<p>我觉得成熟,就是学会接受。</p>
<blockquote>
<p>总而言之,家里蹲也好,尾随狂也好,都是不知道“该放弃的时候必须放弃”这个道理的人,他们这种心智,跟只要大声哭啼就会有奶吃的婴儿有啥区别呢?</p>
</blockquote>
<p>放弃也是一种成熟。</p>
<blockquote>
<p>人类是从什么时候开始觉得有的食物好吃、有的食物不好吃的?</p>
<p>按照熊先生的看法,是从人类不再担心自己会不会别的动物吃掉的时候开始的。</p>
</blockquote>
<p>欲望就是欲望。满足了,便会变化。</p>
<blockquote>
<p>根据最新的科学研究,精子似乎也有着各自不同的作用。<br>首先有一种叫做“取精卵”(“egg-getter”)的精子。它名副其实,作用就是获取卵子,也就是说,它是以受精为目的精子。<br>然后,还有一种叫做“杀手精”(keller)的精子,其作用是向“取精卵”发动进攻将其杀死。自然界里的生物并非都是一夫一妻制,雌性的子宫内可能会有别的雄性的精子。“杀手精”的作用就是把别的雄性的精子杀死,使自己一方的“取卵精”获得优势。另外呢,还有一种与敌人一方的“杀手精”交战、保护自己一方的“取卵精”的精子。</p>
<p>在生命尚处在精子状态时,就已经有了敌我双方,然后为了赢取卵子,展开惨烈的战争。最后,与卵子成功结合而延续了生命的精子,是数亿精子中仅存的一个。</p>
<p>也就是说,生存就是一场你死我活的战斗。《北野武的小酒馆》</p>
</blockquote>
<p>去选择自己的想要的状态,不要后悔。</p>
<blockquote>
<p>不是吃不起寿司,而是付不起小费。一万日元是有的,但手头没有之后的三万日元,是这么一回事。因为他一直按这个规矩做的,所以没钱付小费,他就不去寿司店。</p>
<p>真有腔调</p>
</blockquote>
<p>真有腔调</p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/life/">life</a><a href="/tags/book/">book</a><a href="/tags/小酒馆/">小酒馆</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/04/21/【浅谈】【JAVA多线程】【三】轻量级锁和偏向锁/"><span>【浅谈】【JAVA多线程】【三】轻量级锁和偏向锁</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/04/21/【浅谈】【JAVA多线程】【三】轻量级锁和偏向锁/" rel="bookmark">
<time class="entry-date published" datetime="2018-04-21T02:01:44.000Z">
2018-04-21
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h2 id="轻量级锁和偏向锁"><a href="#轻量级锁和偏向锁" class="headerlink" title="轻量级锁和偏向锁"></a>轻量级锁和偏向锁</h2><blockquote>
<p>轻量级锁</p>
</blockquote>
<p>为什么叫轻量级锁?<br>这其实是对比的状态,轻量级是相对于系统使用<strong>互斥量</strong>来实现的传统锁而言。它并不是用来代替重量锁,而是在某些情境下,比如对于一个同步块,不会存在多余2个线程的竞争,那么轻量级锁性能优势就会比传统锁(也可以叫重量锁)更好点。<br>而它的实现原理是通过<strong>CAS操作</strong>来获取到锁,而不是互斥量。</p>
<blockquote>
<p>偏向锁</p>
</blockquote>
<p>为什么叫偏向锁?<br>偏向锁是在JDK1.6中引入的一项锁优化,它是一种轻量级锁进一步升华。相对比轻量级锁的CAS操作,偏向锁甚至连CAS也不使用,直接在<code>无竞争</code>的情况下,把整个同步都消除。</p>
<blockquote>
<p>Mark Word</p>
</blockquote>
<p>对于JVM(HotSpot)而言,实现轻量级锁和偏向锁使用的是 Mark Word。在JVM中,对象头部分(Object Head)分为两部分,一部分是用于存储对象自身的运行时数据,如哈希码(Hash Code)、GC分代年龄(Generational GC Age)等,这部分数据的长度在32位和64位的虚拟机中分别占32bit和64bit,官方称为“Mark Word”。</p>
<p>在32位的HotSpot虚拟机中对象未锁定的状态下,Mark Word的32bit空间中的25bit用于储存对象的哈希码(Hashcode),4bit用于储存对象分代年龄,2bit用于储存锁标识位,1bit固定为0。</p>
<h4 id="HotSpot虚拟机对象头-Mark-Word"><a href="#HotSpot虚拟机对象头-Mark-Word" class="headerlink" title="HotSpot虚拟机对象头 Mark Word"></a>HotSpot虚拟机对象头 Mark Word</h4><table>
<thead>
<tr>
<th style="text-align:left">储存内容</th>
<th style="text-align:center">标志位</th>
<th style="text-align:right">状态</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">对象哈希码,对象分代年龄</td>
<td style="text-align:center">01</td>
<td style="text-align:right">未锁定</td>
</tr>
<tr>
<td style="text-align:left">指向锁记录的指针</td>
<td style="text-align:center">00</td>
<td style="text-align:right">轻量级锁</td>
</tr>
<tr>
<td style="text-align:left">指向重量级锁的指针</td>
<td style="text-align:center">10</td>
<td style="text-align:right">膨胀(重量级锁定)</td>
</tr>
<tr>
<td style="text-align:left">空,不需要记录信息</td>
<td style="text-align:center">11</td>
<td style="text-align:right">GC标记</td>
</tr>
<tr>
<td style="text-align:left">偏向线程ID,偏向时间戳</td>
<td style="text-align:center">01</td>
<td style="text-align:right">可偏向</td>
</tr>
</tbody>
</table>
<p>简单来说,当线程A去进入同步代码块时,若此时同步对象并没有被锁住(01),那么该线程就可以进入同步块,同时修改修改同步对象的Mark Word,若此时偏向锁可用,则使用偏向锁,若此时线程B也进行获取锁,那么偏向锁就会消除,锁膨胀为轻量级锁,并修改Mark Word,将标示为修改为00,若还有线程C来进行获取锁,那么锁将进一步膨胀,变为重度锁(10)</p>
<p>不论是轻量级锁还是偏向锁,都是根据具体场景而提出的优化,若同步块的竞争较为激烈,那么轻量级锁或者偏向锁反而会降低性能(多出了一步CAS操作)。有时可以通过 <code>-XX: -UseBiasedLocking</code>来关闭偏向锁(在JDK1.6 默认是开启的)。</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="http://item.jd.com/11252778.html" target="_blank" rel="noopener">《深入理解java虚拟机》</a></p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/java多线程/">java多线程</a><a href="/tags/CAS/">CAS</a><a href="/tags/轻量级锁/">轻量级锁</a><a href="/tags/重度锁/">重度锁</a><a href="/tags/Mark-Word/">Mark Word</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/04/17/【浅谈】【JAVA多线程】【二】优化/"><span>【浅谈】【JAVA多线程】【二】自旋锁</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/04/17/【浅谈】【JAVA多线程】【二】优化/" rel="bookmark">
<time class="entry-date published" datetime="2018-04-17T13:38:27.000Z">
2018-04-17
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h2 id="锁的性能优化"><a href="#锁的性能优化" class="headerlink" title="锁的性能优化"></a>锁的性能优化</h2><p>JAVA在高效并发的道路上,一直挺身而进,不断优化升级,这也是为什么多数服务使用java语言的原因之一。</p>
<blockquote>
<p>自旋锁</p>
</blockquote>
<p>为什么会有自旋锁呢?<br>我们知道,当线程遇到在竞争锁时,若未获取锁,则会被挂起。其他线程放弃锁后,又会被再次唤醒。而这一挂一醒都是需要转入内核状态中完成。</p>
<p>而大多数的应用,在竞争锁时,时间都会很短。为了优化这种性能,就出现了这种 <strong>自旋锁</strong>。顾名思义,自旋的意思是,让一个线程在竞争不到锁时,去执行一个 忙循环(可简单理解为一个<code>while(true){}</code>操作)。</p>
<p><strong>自旋锁</strong>在 JDK1.4.2中就已经引入了,默认为关闭状态。<code>-XX:+UseSpinning</code>参数来开启。在JDK1.6中,默认开启。<br>自旋意义在于避免了线程的状态的变更,但是,它并未放弃CPU时间。如果锁的占用时间很短,那么,这种自旋的效果就会很不错。否则,就会白白浪费CPU时间。当然,自旋也不会一直下去。通过设置<code>-XX:PreBlockSpin</code>参数来控制自旋次数,默认为10次。</p>
<p>在JDK1.6中还引入了自适应的自旋锁。JVM开始变得更加聪明,它会根据上一次锁的状态,来更改这次的自旋状态。若自旋等待刚刚获取锁成功,则下一次时,JVM会认为也这次可以自旋获得。若自旋很少成功过,那么JVM也许会放弃自旋这种状态,直接将线程挂起。</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="http://item.jd.com/11252778.html" target="_blank" rel="noopener">《深入理解java虚拟机》</a></p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/多线程/">多线程</a><a href="/tags/java/">java</a><a href="/tags/自旋锁/">自旋锁</a>
</span>
</div>
</div>
</article>
<article>
<h3 class="article-title"><a href="/2018/04/15/【浅谈】【JAVA多线程】【一】synchronized/"><span>【浅谈】【JAVA多线程】【一】synchronized</span></a></h3>
<div class="article-top-meta">
<span class="posted-on">
<a href="/2018/04/15/【浅谈】【JAVA多线程】【一】synchronized/" rel="bookmark">
<time class="entry-date published" datetime="2018-04-15T04:01:13.000Z">
2018-04-15
</time>
</a>
</span>
</div>
<div class="article-content">
<div class="entry">
<h2 id="synchronized-修饰静态方法和实例方法"><a href="#synchronized-修饰静态方法和实例方法" class="headerlink" title="synchronized 修饰静态方法和实例方法"></a>synchronized 修饰静态方法和实例方法</h2><p><strong>synchronized</strong> 字段在java世界中,是较为常见的关键字,其作用简单说,是对一个资源进行操作时,增加了一个排他保护。</p>
<p>其性能在JDK1.6以后增加了偏向锁、自旋锁等优化后已经和<strong>Reentrantlock</strong> 不相上下。偏向锁和自旋锁会在以后的post中再讨论。</p>
<p><strong>synchronized</strong>是一个锁,它要锁的对象必须要搞清楚。一般的用法是用它锁一些共享资源</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">public void lock(){</span><br><span class="line"> synchronized(共享资源对象){</span><br><span class="line"> //do with 共享资源对象</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>除此之外,<strong>synchronized</strong>还可以直接修饰方法,而方法有静态和实例方法之分。那么在修改静态和实例方法时,<strong>synchronized</strong>锁的到底是什么呢?</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//修饰实例方法</span><br><span class="line"> public synchronized void lockInstnaceMethod(){</span><br><span class="line"> // do something...</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//修饰静态方法</span><br><span class="line">public synchronized static void lockStaticMethod(){</span><br><span class="line"> //do something...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这个点,曾经在网易面试中出现过。当然笔试题中并没有直接问你这个有什么区别,而是写了大量的代码让你判断输出结果,如果对这个点比较模糊的话,可能会被这个笔试题吓到(这个题目出现在了笔试题中的最后一个,压轴哦)。</p>
<p>简言而之,在修饰静态方法时,<strong>synchronized</strong><code>锁的是这个类</code>,而修饰实例方法时,锁的是<code>这个类的实例(可以理解为锁的是这个实例this)</code>。</p>
<p>修饰实例方法,若不同的线程持有的实例不同,那么这俩个线程不会存在竞争关系。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">public class ThreadTest {</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> public synchronized static void lockStaticMethod() {</span><br><span class="line"> //do something</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public synchronized void lockInstanceMethod() {</span><br><span class="line"> //do something</span><br><span class="line"> }</span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"></span><br><span class="line"> final ThreadTest instanceA = new ThreadTest();</span><br><span class="line"> final ThreadTest instanceB = new ThreadTest();</span><br><span class="line"></span><br><span class="line"> Thread A = new Thread() {</span><br><span class="line"> @Override</span><br><span class="line"> public void run() {</span><br><span class="line"> instanceA.lockInstanceMethod();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> Thread B = new Thread() {</span><br><span class="line"> @Override</span><br><span class="line"> public void run() {</span><br><span class="line"> instanceB.lockInstanceMethod();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> A.start();</span><br><span class="line"> B.start();</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>线程A和线程B不存在竞争关系,因为持有的实例对象是完全两个不同的对象。</p>
<figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class ThreadTest {</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> public synchronized static void lockStaticMethod() {</span><br><span class="line"> //do something</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public synchronized void lockInstanceMethod() {</span><br><span class="line"> //do something</span><br><span class="line"> }</span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"></span><br><span class="line"> final ThreadTest instanceA = new ThreadTest();</span><br><span class="line"> final ThreadTest instanceB = new ThreadTest();</span><br><span class="line"></span><br><span class="line"> Thread A = new Thread() {</span><br><span class="line"> @Override</span><br><span class="line"> public void run() {</span><br><span class="line"> ThreadTest.lockStaticMethod();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> Thread B = new Thread() {</span><br><span class="line"> @Override</span><br><span class="line"> public void run() {</span><br><span class="line"> ThreadTest.lockStaticMethod();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> A.start();</span><br><span class="line"> B.start();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>线程A和线程B存在竞争关系,因为<strong>synchronized</strong>锁的是ThreadTest这个类。</p>
</div>
</div>
<div class="article-footer">
<div class="article-meta pull-left">
<span class="post-tags">
<i class="icon-tags"></i>
<a href="/tags/多线程/">多线程</a><a href="/tags/锁/">锁</a><a href="/tags/java基础/">java基础</a><a href="/tags/网易面试题/">网易面试题</a>
</span>
</div>
</div>
</article>
<nav class="pagination">
<a href="/page/2/" class="pagination-next">下一页</a>
</nav>
</main>
<footer class="site-footer">
<p class="site-info">
Proudly powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and
Theme by <a href="https://github.com/CodeDaraW/Hacker" target="_blank">Hacker</a>
</br>
© 2020 wanglei
</p>
</footer>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-83609983-2', 'auto');
ga('send', 'pageview');
</script>
</div>
</div>
</body>
</html>