-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconcurrent.html
More file actions
822 lines (748 loc) · 122 KB
/
concurrent.html
File metadata and controls
822 lines (748 loc) · 122 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
<html><head><meta charset="utf-8"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.8.3/katex.min.css" integrity="sha384-B41nY7vEWuDrE9Mr+J2nBL0Liu+nl/rBXTdpQal730oTHdlrlXHzYMOhDU60cwde" crossorigin="anonymous"><style>.markdown-body hr::after,.markdown-body::after{clear:both}.loopLine,.messageLine0{marker-end:"url(#arrowhead)"}@font-face{font-family:octicons-link;src:url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff')}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;color:#24292e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";line-height:1.5;word-wrap:break-word}.markdown-body .pl-c{color:#6a737d}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:#005cc5}.markdown-body .pl-e,.markdown-body .pl-en{color:#6f42c1}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:#24292e}.markdown-body .pl-ent{color:#22863a}.markdown-body .pl-k{color:#d73a49}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:#032f62}.markdown-body .pl-smw,.markdown-body .pl-v{color:#e36209}.markdown-body .pl-bu{color:#b31d28}.markdown-body .pl-ii{color:#fafbfc;background-color:#b31d28}.markdown-body .pl-c2{color:#fafbfc;background-color:#d73a49}.markdown-body .pl-c2::before{content:"^M"}.markdown-body .pl-sr .pl-cce{font-weight:700;color:#22863a}.markdown-body .pl-ml{color:#735c0f}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:#005cc5}.markdown-body .pl-mi{font-style:italic;color:#24292e}.markdown-body .pl-mb{font-weight:700;color:#24292e}.markdown-body .pl-md{color:#b31d28;background-color:#ffeef0}.markdown-body .pl-mi1{color:#22863a;background-color:#f0fff4}.markdown-body .pl-mc{color:#e36209;background-color:#ffebda}.markdown-body .pl-mi2{color:#f6f8fa;background-color:#005cc5}.markdown-body .pl-mdr{font-weight:700;color:#6f42c1}.markdown-body .pl-ba{color:#586069}.markdown-body .pl-sg{color:#959da5}.markdown-body .pl-corl{text-decoration:underline;color:#032f62}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body hr::after,.markdown-body hr::before,.markdown-body::after,.markdown-body::before{display:table;content:""}.markdown-body a{background-color:transparent;-webkit-text-decoration-skip:objects;color:#0366d6;text-decoration:none}.markdown-body a:active,.markdown-body a:hover{outline-width:0}.markdown-body h1{margin:.67em 0}.markdown-body img{border-style:none}.markdown-body svg:not(:root){overflow:hidden}.markdown-body hr{box-sizing:content-box}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=checkbox]{box-sizing:border-box;padding:0}.markdown-body *{box-sizing:border-box}.markdown-body a:hover{text-decoration:underline}.markdown-body strong{font-weight:600}.markdown-body td,.markdown-body th{padding:0}.markdown-body blockquote{margin:0}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body .task-list-item,ul.table-of-contents{list-style-type:none}.markdown-body dd{margin-left:0}.markdown-body code{font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace}.markdown-body pre{font:12px SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;word-wrap:normal}.markdown-body .pl-0{padding-left:0!important}.markdown-body .pl-1{padding-left:4px!important}.markdown-body .pl-2{padding-left:8px!important}.markdown-body .pl-3{padding-left:16px!important}.markdown-body .pl-4{padding-left:24px!important}.markdown-body .pl-5{padding-left:32px!important}.markdown-body .pl-6{padding-left:40px!important}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body hr{overflow:hidden;background:#e1e4e8;height:.25em;padding:0;margin:24px 0;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d;border-left:.25em solid #dfe2e5}.markdown-body h1,.markdown-body h2{padding-bottom:.3em;border-bottom:1px solid #eaecef}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h6{color:#6a737d}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table th{font-weight:600}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #dfe2e5}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body code{padding:.2em 0;margin:0;font-size:85%;background-color:rgba(27,31,35,.05);border-radius:3px}.markdown-body code::after,.markdown-body code::before{letter-spacing:-.2em;content:"\00a0"}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f6f8fa;border-radius:3px}.markdown-body pre code{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body pre code::after,.markdown-body pre code::before{content:normal}.markdown-body .full-commit .btn-outline:not(:disabled):hover{color:#005cc5;border-color:#005cc5}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:1px solid #d1d5da;border-bottom-color:#c6cbd1;border-radius:3px;box-shadow:inset 0 -1px 0 #c6cbd1}.node text,.noteText,div.mermaidTooltip{font-family:'trebuchet ms',verdana,arial}.markdown-body :checked+.radio-label{position:relative;z-index:1;border-color:#0366d6}.markdown-body .task-list-item+.task-list-item{margin-top:3px}.markdown-body .task-list-item input{margin:0 .2em .25em -1.6em;vertical-align:middle}.markdown-body hr{border-bottom-color:#eee}.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-meta,.hljs-strong{font-weight:700}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.mermaid .label{color:#333}.node circle,.node ellipse,.node polygon,.node rect{fill:#ECECFF;stroke:#CCF;stroke-width:1px}.edgePath .path{stroke:#333}.edgeLabel{background-color:#e8e8e8}.cluster rect{fill:#ffffde!important;rx:4!important;stroke:#aa3!important;stroke-width:1px!important}.cluster text{fill:#333}.actor{stroke:#CCF;fill:#ECECFF}text.actor{fill:#000;stroke:none}.actor-line{stroke:grey}.messageLine0,.messageLine1{stroke-width:1.5;stroke-dasharray:"2 2";stroke:#333}#arrowhead{fill:#333}#crosshead path{fill:#333!important;stroke:#333!important}.messageText{fill:#333;stroke:none}.labelBox{stroke:#CCF;fill:#ECECFF}.labelText,.loopText{fill:#000;stroke:none}.loopLine{stroke-width:2;stroke-dasharray:"2 2";stroke:#CCF}.note{stroke:#aa3;fill:#fff5ad}.noteText{fill:#000;stroke:none;font-size:14px}.section{stroke:none;opacity:.2}.section0{fill:rgba(102,102,255,.49)}.section2{fill:#fff400}.section1,.section3{fill:#fff;opacity:.2}.sectionTitle0,.sectionTitle1,.sectionTitle2,.sectionTitle3{fill:#333}.sectionTitle{text-anchor:start;font-size:11px;text-height:14px}.grid .tick{stroke:#d3d3d3;opacity:.3;shape-rendering:crispEdges}.grid path{stroke-width:0}.today{fill:none;stroke:red;stroke-width:2px}.task{stroke-width:2}.taskText{text-anchor:middle;font-size:11px}.taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px}.taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}.taskText0,.taskText1,.taskText2,.taskText3{fill:#fff}.task0,.task1,.task2,.task3{fill:#8a90dd;stroke:#534fbc}.taskTextOutside0,.taskTextOutside1,.taskTextOutside2,.taskTextOutside3{fill:#000}.active0,.active1,.active2,.active3{fill:#bfc7ff;stroke:#534fbc}.activeText0,.activeText1,.activeText2,.activeText3{fill:#000!important}.done0,.done1,.done2,.done3{stroke:grey;fill:#d3d3d3;stroke-width:2}.doneText0,.doneText1,.doneText2,.doneText3{fill:#000!important}.crit0,.crit1,.crit2,.crit3{stroke:#f88;fill:red;stroke-width:2}.activeCrit0,.activeCrit1,.activeCrit2,.activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}.doneCrit0,.doneCrit1,.doneCrit2,.doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}.activeCritText0,.activeCritText1,.activeCritText2,.activeCritText3,.doneCritText0,.doneCritText1,.doneCritText2,.doneCritText3{fill:#000!important}.titleText{text-anchor:middle;font-size:18px;fill:#000}.node text{font-size:14px}div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}@font-face{font-family:KaTeX_AMS;src:url(fonts/KaTeX_AMS-Regular.eot);src:url(fonts/KaTeX_AMS-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_AMS-Regular.woff2) format('woff2'),url(fonts/KaTeX_AMS-Regular.woff) format('woff'),url(fonts/KaTeX_AMS-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Bold.eot);src:url(fonts/KaTeX_Caligraphic-Bold.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Caligraphic-Bold.woff2) format('woff2'),url(fonts/KaTeX_Caligraphic-Bold.woff) format('woff'),url(fonts/KaTeX_Caligraphic-Bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Regular.eot);src:url(fonts/KaTeX_Caligraphic-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Caligraphic-Regular.woff2) format('woff2'),url(fonts/KaTeX_Caligraphic-Regular.woff) format('woff'),url(fonts/KaTeX_Caligraphic-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Bold.eot);src:url(fonts/KaTeX_Fraktur-Bold.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Fraktur-Bold.woff2) format('woff2'),url(fonts/KaTeX_Fraktur-Bold.woff) format('woff'),url(fonts/KaTeX_Fraktur-Bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Regular.eot);src:url(fonts/KaTeX_Fraktur-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Fraktur-Regular.woff2) format('woff2'),url(fonts/KaTeX_Fraktur-Regular.woff) format('woff'),url(fonts/KaTeX_Fraktur-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Bold.eot);src:url(fonts/KaTeX_Main-Bold.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Main-Bold.woff2) format('woff2'),url(fonts/KaTeX_Main-Bold.woff) format('woff'),url(fonts/KaTeX_Main-Bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Italic.eot);src:url(fonts/KaTeX_Main-Italic.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Main-Italic.woff2) format('woff2'),url(fonts/KaTeX_Main-Italic.woff) format('woff'),url(fonts/KaTeX_Main-Italic.ttf) format('truetype');font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Regular.eot);src:url(fonts/KaTeX_Main-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Main-Regular.woff2) format('woff2'),url(fonts/KaTeX_Main-Regular.woff) format('woff'),url(fonts/KaTeX_Main-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-Italic.eot);src:url(fonts/KaTeX_Math-Italic.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Math-Italic.woff2) format('woff2'),url(fonts/KaTeX_Math-Italic.woff) format('woff'),url(fonts/KaTeX_Math-Italic.ttf) format('truetype');font-weight:400;font-style:italic}@font-face{font-family:KaTeX_SansSerif;src:url(fonts/KaTeX_SansSerif-Regular.eot);src:url(fonts/KaTeX_SansSerif-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_SansSerif-Regular.woff2) format('woff2'),url(fonts/KaTeX_SansSerif-Regular.woff) format('woff'),url(fonts/KaTeX_SansSerif-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(fonts/KaTeX_Script-Regular.eot);src:url(fonts/KaTeX_Script-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Script-Regular.woff2) format('woff2'),url(fonts/KaTeX_Script-Regular.woff) format('woff'),url(fonts/KaTeX_Script-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(fonts/KaTeX_Size1-Regular.eot);src:url(fonts/KaTeX_Size1-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size1-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size1-Regular.woff) format('woff'),url(fonts/KaTeX_Size1-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(fonts/KaTeX_Size2-Regular.eot);src:url(fonts/KaTeX_Size2-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size2-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size2-Regular.woff) format('woff'),url(fonts/KaTeX_Size2-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(fonts/KaTeX_Size3-Regular.eot);src:url(fonts/KaTeX_Size3-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size3-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size3-Regular.woff) format('woff'),url(fonts/KaTeX_Size3-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(fonts/KaTeX_Size4-Regular.eot);src:url(fonts/KaTeX_Size4-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size4-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size4-Regular.woff) format('woff'),url(fonts/KaTeX_Size4-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(fonts/KaTeX_Typewriter-Regular.eot);src:url(fonts/KaTeX_Typewriter-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Typewriter-Regular.woff2) format('woff2'),url(fonts/KaTeX_Typewriter-Regular.woff) format('woff'),url(fonts/KaTeX_Typewriter-Regular.ttf) format('truetype');font-weight:400;font-style:normal}.katex-display{display:block;margin:1em 0;text-align:center}.katex .base,.katex .katex-html,.katex .strut{display:inline-block}.katex-display>.katex{display:inline-block;text-align:initial}.katex{font:400 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;white-space:nowrap;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .base,.katex .vlist,.katex .vlist>span{position:relative}.katex .mathrm{font-style:normal}.katex .mainit,.katex .mathit,.katex .textit{font-style:italic}.katex .mathit{font-family:KaTeX_Math}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .amsrm,.katex .mathbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr{font-family:KaTeX_Script}.katex .mathsf{font-family:KaTeX_SansSerif}.katex .mainit,.katex .mainrm{font-family:KaTeX_Main}.katex .mainrm{font-style:normal}.katex .mord+.mop{margin-left:.16667em}.katex .mord+.mbin{margin-left:.22222em}.katex .mord+.mrel{margin-left:.27778em}.katex .mop+.mop,.katex .mop+.mord,.katex .mord+.minner{margin-left:.16667em}.katex .mop+.mrel{margin-left:.27778em}.katex .mop+.minner{margin-left:.16667em}.katex .mbin+.minner,.katex .mbin+.mop,.katex .mbin+.mopen,.katex .mbin+.mord{margin-left:.22222em}.katex .mrel+.minner,.katex .mrel+.mop,.katex .mrel+.mopen,.katex .mrel+.mord{margin-left:.27778em}.katex .mclose+.mop{margin-left:.16667em}.katex .mclose+.mbin{margin-left:.22222em}.katex .mclose+.mrel{margin-left:.27778em}.katex .mclose+.minner,.katex .minner+.mop,.katex .minner+.mord,.katex .mpunct+.mclose,.katex .mpunct+.minner,.katex .mpunct+.mop,.katex .mpunct+.mopen,.katex .mpunct+.mord,.katex .mpunct+.mpunct,.katex .mpunct+.mrel{margin-left:.16667em}.katex .minner+.mbin{margin-left:.22222em}.katex .minner+.mrel{margin-left:.27778em}.katex .minner+.minner,.katex .minner+.mopen,.katex .minner+.mpunct{margin-left:.16667em}.katex .mbin.mtight,.katex .mclose.mtight,.katex .minner.mtight,.katex .mop.mtight,.katex .mopen.mtight,.katex .mord.mtight,.katex .mpunct.mtight,.katex .mrel.mtight{margin-left:0}.katex .mclose+.mop.mtight,.katex .minner+.mop.mtight,.katex .mop+.mop.mtight,.katex .mop+.mord.mtight,.katex .mord+.mop.mtight{margin-left:.16667em}.katex .vlist-t{display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom}.katex .vlist>span{display:block;height:0}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px}.katex .fontsize-ensurer,.katex .llap>.fix,.katex .mfrac .frac-line,.katex .mspace,.katex .mtable .arraycolsep,.katex .mtable .vertical-separator,.katex .nulldelimiter,.katex .rlap>.fix,.katex .rule,.katex .sizing{display:inline-block}.katex .msupsub{text-align:left}.katex .accent>.vlist-t,.katex .mfrac>span>span,.katex .mtable .col-align-c>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .mfrac .frac-line{width:100%;border-bottom-style:solid}.katex .mspace.negativethinspace{margin-left:-.16667em}.katex .mspace.thinspace{width:.16667em}.katex .mspace.negativemediumspace{margin-left:-.22222em}.katex .mspace.mediumspace{width:.22222em}.katex .mspace.thickspace{width:.27778em}.katex .mspace.sixmuspace{width:.333333em}.katex .mspace.eightmuspace{width:.444444em}.katex .mspace.enspace{width:.5em}.katex .mspace.twelvemuspace{width:.666667em}.katex .mspace.quad{width:1em}.katex .mspace.qquad{width:2em}.katex .llap,.katex .rlap{width:0;position:relative}.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .llap>.inner{right:0}.katex .rlap>.inner{left:0}.katex .katex-logo .a{font-size:.75em;margin-left:-.32em;position:relative;top:-.2em}.katex .katex-logo .t{margin-left:-.23em}.katex .katex-logo .e{margin-left:-.1667em;position:relative;top:.2155em}.katex .katex-logo .x{margin-left:-.125em}.katex .rule{border:0 solid;position:relative}.katex .overline .overline-line,.katex .underline .underline-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent .accent-body>span{width:0}.katex .accent .accent-body.accent-vec>span{position:relative;left:.326em}.katex .accent .accent-body.accent-hungarian>span{position:relative;left:.25em}.katex .mtable .vertical-separator{margin:0 -.025em;border-right:.05em solid #000}.katex .stretchy,.katex svg{width:100%;display:block}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex .mover,.katex .munder,.katex .x-arrow,div.mermaid{text-align:center}.katex svg{position:absolute}.katex svg path{fill:currentColor}.katex svg line{stroke:currentColor}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .x-arrow-pad{padding:0 .5em}.katex .boxpad{padding:0 .3em}.katex .fbox{box-sizing:border-box;border:.04em solid #000}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap+.mbin,.katex .cancel-lap+.mord,.katex .cancel-lap+.msupsub,.katex .mbin+.cancel-lap,.katex .mord+.cancel-lap{margin-left:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}body{margin:0;padding:0}.markdown-body{min-width:256px;max-width:978px;margin:0 auto;padding:20px;font-size:14px}.markdown-body h1{font-size:2.25em}.markdown-body h2{font-size:1.75em}.markdown-body h3{font-size:1.5em}.markdown-body h4{font-size:1.25em}.markdown-body h5,.markdown-body h6{font-size:1em}div.mermaid svg{height:auto}hr.footnotes-sep{margin:64px 0 32px;height:1px}.footnotes{font-size:90%;padding-left:16px}li.footnote-item>p{margin:8px 0}.danger,.info,.success,.warning{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.danger>p:last-child,.info>p:last-child,.success>p:last-child,.warning>p:last-child{margin-bottom:0}.success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}abbr[title]{cursor:help;border-bottom:1px dotted #777}ul.table-of-contents li{margin:4px 0}.markdown-body table{border-spacing:0;border-collapse:collapse;width:auto;display:table;}.markdown-body table td,.markdown-body table th{word-break:break-all}</style></head><article class="markdown-body" id="preview" data-open-title="Hide Preview" data-closed-title="Show Preview" style="padding-bottom: 0px;"><div><p data-source-line="1"><ul class="toc">
<li>
<a href="#并发">并发</a>
<ul>
<li>
<a href="#并发在一段时间内多个程序一起执行">并发:在一段时间内,多个程序一起执行</a>
<ul>
<li>
<a href="#实现并发的思路">实现并发的思路:</a>
</li>
</ul>
</li>
<li>
<a href="#并行在一个时刻多个程序同时执行">并行:在一个时刻,多个程序同时执行</a>
</li>
<li>
<a href="#区别">区别:</a>
</li>
</ul>
</li>
<li>
<a href="#为什么通常采用多线程而非多进程">为什么通常采用多线程而非多进程?</a>
</li>
<li>
<a href="#线程是怎么实现的">线程是怎么实现的?</a>
<ul>
<li>
<a href="#内核线程kernel-level-threadklt实现">内核线程(Kernel-Level Thread,KLT)实现</a>
</li>
<li>
<a href="#用户线程实现"><s>用户线程实现</s></a>
</li>
<li>
<a href="#用户线程加轻量级进程混合实现">用户线程加轻量级进程混合实现</a>
</li>
</ul>
</li>
<li>
<a href="#线程是如何调度的">线程是如何调度的?</a>
<ul>
<li>
<a href="#协同式调度">协同式调度</a>
</li>
<li>
<a href="#抢占式调度">抢占式调度</a>
</li>
</ul>
</li>
<li>
<a href="#java-线程的优先级">Java 线程的优先级</a>
</li>
<li>
<a href="#java-线程的状态">Java 线程的状态</a>
<ul>
<li>
<a href="#状态的定义与流转">状态的定义与流转:</a>
</li>
<li>
<a href="#blocked-状态可能的原因">BLOCKED 状态可能的原因:</a>
</li>
<li>
<a href="#blocked-与-waiting-的区别">BLOCKED 与 WAITING 的区别</a>
</li>
</ul>
</li>
<li>
<a href="#java线程的其他知识">Java线程的其他知识</a>
<ul>
<li>
<ul>
<li>
<a href="#runable-thread">Runable、Thread</a>
</li>
<li>
<a href="#线程基本操作">线程基本操作</a>
<ul>
<li>
<a href="#新建线程-threadstart">新建线程-Thread.start()</a>
</li>
<li>
<a href="#终止线程-threadstop">终止线程-<s>Thread.stop()</s></a>
</li>
<li>
<a href="#线程中断-threadinterrupt">线程中断-Thread.interrupt</a>
</li>
<li>
<a href="#等待与通知waitnotify">等待与通知——wait()/notify()</a>
</li>
<li>
<a href="#线程挂起-threadsuspend">线程挂起-<s>Thread.suspend()</s></a>
</li>
<li>
<a href="#join和yeild">join和yeild</a>
</li>
<li>
<a href="#守护线程-threadsetdeamontrue">守护线程-Thread.setDeamon(true)</a>
</li>
</ul>
</li>
<li>
<a href="#线程安全-与-线程不安全">线程安全 与 线程不安全</a>
</li>
<li>
<a href="#进程间通信">进程间通信</a>
<ul>
<li>
<a href="#管道-pipe">管道( pipe )</a>
</li>
<li>
<a href="#信号量-semophore">信号量( semophore )</a>
</li>
<li>
<a href="#消息队列-message-queue">消息队列( message queue )</a>
</li>
<li>
<a href="#信号-sinal">信号 ( sinal )</a>
</li>
<li>
<a href="#共享内存-shared-memory">共享内存( shared memory )</a>
</li>
<li>
<a href="#套接字-socket">套接字( socket )</a>
</li>
</ul>
</li>
<li>
<a href="#线程间通信">线程间通信</a>
<ul>
<li>
<a href="#1-通过共享对象通信">1、通过共享对象通信</a>
</li>
<li>
<a href="#2-忙等待">2、忙等待</a>
</li>
<li>
<a href="#3-waitnotify和notifyall">3、wait(),notify()和notifyAll()</a>
<ul>
<li>
<a href="#例-1通知其他线程">例 1:通知其他线程</a>
</li>
<li>
<a href="#例-2使用wait-notify通知">例 2:使用wait()、notify()通知</a>
</li>
<li>
<a href="#例-3利用countdownlatch类实现">例 3:利用CountDownLatch类实现</a>
</li>
</ul>
</li>
<li>
<a href="#4-丢失的信号">4、丢失的信号</a>
</li>
<li>
<a href="#5-假唤醒">5、假唤醒</a>
</li>
<li>
<a href="#6-多线程等待相同信号">6、多线程等待相同信号</a>
</li>
<li>
<a href="#7-不要对常量字符串或全局对象调用wait">7、不要对常量字符串或全局对象调用wait()</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#同步和异步">同步和异步:</a>
</li>
<li>
<a href="#临界区">临界区:</a>
</li>
<li>
<a href="#死锁-饥饿-活锁">死锁、饥饿、活锁:</a>
<ul>
<li>
<a href="#死锁">死锁</a>
<ul>
<li>
<a href="#死锁发生的条件">死锁发生的条件?</a>
</li>
<li>
<a href="#解决死锁问题">解决死锁问题</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#多线程的特性">多线程的特性:</a>
<ul>
<li>
<a href="#原子性synchronized">原子性:synchronized</a>
</li>
<li>
<a href="#可见性volatile">可见性:volatile</a>
</li>
<li>
<a href="#有序性">有序性:</a>
</li>
</ul>
</li>
</ul>
</li></ul></p><p data-source-line="3">参考:</p><ul data-source-line="4"><li><a href="http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html" target="_blank">进程与线程</a></li><li><a href="https://segmentfault.com/a/1190000000398256#shared_object" target="_blank">JAVA线程间通信简介</a></li><li><a href="https://my.oschina.net/hosee/blog/599000" target="_blank">[高并发Java 二] 多线程基础</a></li><li><a href="https://my.oschina.net/hosee/blog/597934" target="_blank">[高并发Java 一] 前言</a></li></ul><h1 data-source-line="9" id="并发"><a class="markdownIt-Anchor" href="#并发"></a>并发</h1><h2 data-source-line="11" id="并发在一段时间内多个程序一起执行"><a class="markdownIt-Anchor" href="#并发在一段时间内多个程序一起执行"></a>并发:在一段时间内,多个程序一起执行</h2><p data-source-line="13">当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。这种方式我们称之为并发(Concurrent)。</p><p data-source-line="15">偏重于多个任务交替执行,而多个任务之间有可能还是串行的,而并行是真正意义上的“同时执行”。</p><p data-source-line="17">并发通常是指在单处理器上提高程序的性能。
如果没有<mark>阻塞</mark>,那么在单处理器机器上使用并发就没有什么意义了。</p><h3 data-source-line="20" id="实现并发的思路"><a class="markdownIt-Anchor" href="#实现并发的思路"></a>实现并发的思路:</h3><ol data-source-line="22"><li>每个任务作为进程在其自己的地址空间中执行。也就是实现并发并不一定只有多线程这一个思路,还可以多进程去实现。</li><li>在执行程序表示的单一进程中创建任务。</li></ol><h2 data-source-line="25" id="并行在一个时刻多个程序同时执行"><a class="markdownIt-Anchor" href="#并行在一个时刻多个程序同时执行"></a>并行:在一个时刻,多个程序同时执行</h2><p data-source-line="27">当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。</p><h2 data-source-line="29" id="区别"><a class="markdownIt-Anchor" href="#区别"></a>区别:</h2><p data-source-line="31">并发和并行是即相似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可以同时执行。</p><h1 data-source-line="33" id="为什么通常采用多线程而非多进程"><a class="markdownIt-Anchor" href="#为什么通常采用多线程而非多进程"></a>为什么通常采用多线程而非多进程?</h1><p data-source-line="35">因为多进程比多线程更消耗资源。进程的切换是非常重量级的操作,非常消耗资源。各个线程既可以共享进程资源(内存地址、文件 IO 等),又可以独立调度(线程是 CPU 调度的基本单位),所以线程会较为广泛的用于并发设计。</p><p data-source-line="37">另外,操作系统的设计,可以归结为三点:</p><ul data-source-line="38"><li>以多进程形式,允许多个任务同时运行;</li><li>以多线程形式,允许单个任务分成不同的部分运行;</li><li>提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。</li></ul><h1 data-source-line="42" id="线程是怎么实现的"><a class="markdownIt-Anchor" href="#线程是怎么实现的"></a>线程是怎么实现的?</h1><p data-source-line="44">目前主流的操作系统都提供了线程实现,java则提供的线程实现方法都是native的,因为不同的硬件和操作系统提供线程调度方式并不尽相同,所以java没用采用和平台无关的统一手段来实现。</p><h2 data-source-line="46" id="内核线程kernel-level-threadklt实现"><a class="markdownIt-Anchor" href="#内核线程kernel-level-threadklt实现"></a>内核线程(Kernel-Level Thread,KLT)实现</h2><p data-source-line="48">就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换。</p><p data-source-line="50">但是程序一般不会直接使用内核线程,而是去使用内核线程的一种高级接口—轻量级进程(Light Weight Process,LWP),轻量级进程就是我们所讲的线程,这种轻量级进程与内核线程之间1:1的对应关系。</p><p data-source-line="52"><img data-src="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42161%2FC15E04DF175A4F7E9FD16C495BAD656A" alt="image" src="http://note.youdao.com/yws/res/42161/C15E04DF175A4F7E9FD16C495BAD656A?ynotemdtimestamp=1562307580979" data-processed="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42161%2FC15E04DF175A4F7E9FD16C495BAD656A"></p><p data-source-line="54">Strength:</p><ul data-source-line="55"><li>内核直接支持,由操作系统内核创建和撤销。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于I/O操作而阻塞,不会影响其它线程的运行。</li></ul><p data-source-line="57">Weaknesses:</p><ul data-source-line="58"><li>线程的操作、创建、同步等都需要系统调用,而系统调用代价比较高,需要在用户态和内核态中来回切换。</li><li>每个轻量级的进程都需要一个内核线程来支持,需要消耗一定的内核资源,因此 LWP 是有限的。</li></ul><h2 data-source-line="61" id="用户线程实现"><a class="markdownIt-Anchor" href="#用户线程实现"></a><s>用户线程实现</s></h2><p data-source-line="63">用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。</p><p data-source-line="65"><img data-src="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42184%2F65F24BF661D0425A9219F9FF803D3296" alt="image" src="http://note.youdao.com/yws/res/42184/65F24BF661D0425A9219F9FF803D3296?ynotemdtimestamp=1562307580979" data-processed="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42184%2F65F24BF661D0425A9219F9FF803D3296"></p><p data-source-line="67">S:</p><ul data-source-line="68"><li>切换由用户态程序自己控制内核切换,不需要内核干涉,少了进出内核态的消耗。</li></ul><p data-source-line="70">W:</p><ul data-source-line="71"><li>没有系统内核的支援,所有的线程操作都需要自己处理,java曾经用过,不过最后还是放弃了。</li></ul><h2 data-source-line="73" id="用户线程加轻量级进程混合实现"><a class="markdownIt-Anchor" href="#用户线程加轻量级进程混合实现"></a>用户线程加轻量级进程混合实现</h2><p data-source-line="75">S:</p><ul data-source-line="76"><li>这种混合模式下,既存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等依然廉价,可以支持大规模的用户线程并发。</li><li>操作系统提供支持的轻量进程作为用户线程和内核线程之间的桥梁,用户线程的系统调用要通过轻量级线程来完成,大大降低了进程阻塞的风险。用户线程和轻量级进程比是N:M多对对的关系。</li></ul><p data-source-line="79"><img data-src="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42178%2F2D3B728F85824EE5B1EC39D9E5C95BAC" alt="image" src="http://note.youdao.com/yws/res/42178/2D3B728F85824EE5B1EC39D9E5C95BAC?ynotemdtimestamp=1562307580979" data-processed="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42178%2F2D3B728F85824EE5B1EC39D9E5C95BAC"></p><h1 data-source-line="81" id="线程是如何调度的"><a class="markdownIt-Anchor" href="#线程是如何调度的"></a>线程是如何调度的?</h1><p data-source-line="83">线程调度主要是指系统为线程分配处理器使用权的过程,主要分为:协同式线程调度和抢占式线程调度。</p><h2 data-source-line="85" id="协同式调度"><a class="markdownIt-Anchor" href="#协同式调度"></a>协同式调度</h2><p data-source-line="87">线程的执行时间由线程本身来控制,线程把自己的工作执行完成以后,主动通知系统切换到另一个线程上。像lua的“协同历程”就是如此实现的。</p><p data-source-line="89">S:</p><ul data-source-line="90"><li>实现简单,线程把自己的事情干完后进行线程切换,切换操作对线程自己是可知的。无同步问题</li></ul><p data-source-line="92">W:</p><ul data-source-line="93"><li>线程执行时间不可控制,如果某个线程出现问题阻塞,会造成程序阻塞。</li><li>这几乎可以认为是一个扯淡的调度方式,执行时间不可控是肯定不能接受的,如果要是使用这种方式肯定还得需要其他方面的限制。存在即合理吧~</li></ul><h2 data-source-line="96" id="抢占式调度"><a class="markdownIt-Anchor" href="#抢占式调度"></a>抢占式调度</h2><p data-source-line="98">抢占式线程调度中每个线程<strong>由系统来分配执行时间</strong>,线程的切换不由线程本身来决定。所以不像协同式调度那样,出现单个线程阻塞造成整个进程阻塞的问题。Java就是采用这种方式。</p><p data-source-line="100">但是既然由系统分配时间,如果某线程想要更多的执行时间怎么办?答案就是设置优先级。</p><h1 data-source-line="102" id="java-线程的优先级"><a class="markdownIt-Anchor" href="#java-线程的优先级"></a>Java 线程的优先级</h1><p data-source-line="104">优先级代表了抢占CPU机会的大小。</p><p data-source-line="106">Thread类中有3个变量定义了线程优先级。(总共有0-10个优先级,级别为0的是分配给JVM的,应用程序不能设置该级别)</p><pre data-source-line="108"><code class="hljs"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> MIN_PRIORITY = <span class="hljs-number">1</span>;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> NORM_PRIORITY = <span class="hljs-number">5</span>;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> MAX_PRIORITY = <span class="hljs-number">10</span>;
</code></pre><p data-source-line="114">不同的操作系统有着不同的优先级,例如Linux有140个优先级,Windows有7个,Java又是跨平台的,所以JAVA相当于用着10个优先级做了一个<strong>粗略的映射</strong>(所以可以认为是有点稍微不靠谱的)。</p><pre data-source-line="116"><code class="hljs"><span class="hljs-keyword">package</span> test;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Test
{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> High <span class="hljs-keyword">extends</span> Thread
{
<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-keyword">count</span> = <span class="hljs-number">0</span>;
@Override
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run()
{
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>)
{
<span class="hljs-keyword">synchronized</span> (Test.<span class="hljs-keyword">class</span>)
{
<span class="hljs-keyword">count</span>++;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">count</span> > <span class="hljs-number">10000000</span>)
{
System.out.<span class="hljs-keyword">println</span>(<span class="hljs-string">"High"</span>);
<span class="hljs-keyword">break</span>;
}
}
}
}
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> Low <span class="hljs-keyword">extends</span> Thread
{
<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-keyword">count</span> = <span class="hljs-number">0</span>;
@Override
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run()
{
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>)
{
<span class="hljs-keyword">synchronized</span> (Test.<span class="hljs-keyword">class</span>)
{
<span class="hljs-keyword">count</span>++;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">count</span> > <span class="hljs-number">10000000</span>)
{
System.out.<span class="hljs-keyword">println</span>(<span class="hljs-string">"Low"</span>);
<span class="hljs-keyword">break</span>;
}
}
}
}
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(String[] args) <span class="hljs-keyword">throws</span> InterruptedException
{
High high = <span class="hljs-keyword">new</span> High();
Low low = <span class="hljs-keyword">new</span> Low();
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();
}
}
</code></pre><p data-source-line="173">让一个高优先级的线程和低优先级的线程同时争夺一个锁,看看哪个最先完成。
当然并不一定是高优先级一定先完成。再多次运行后发现,高优先级完成的<mark>概率比较大</mark>,但是低优先级还是有可能先完成的。</p><p data-source-line="176">既然都已经有机会执行线程了,那么接下来肯定是如何更好的利用线程了,所以 JAVA 又针对线程定义了一些状态和状态之间的流转</p><h1 data-source-line="178" id="java-线程的状态"><a class="markdownIt-Anchor" href="#java-线程的状态"></a>Java 线程的状态</h1><h2 data-source-line="180" id="状态的定义与流转"><a class="markdownIt-Anchor" href="#状态的定义与流转"></a>状态的定义与流转:</h2><p data-source-line="182"><img data-src="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42223%2F3259B40DE0D0411984701E672AEB9942" alt="image" src="http://note.youdao.com/yws/res/42223/3259B40DE0D0411984701E672AEB9942?ynotemdtimestamp=1562307580979" data-processed="http%3A%2F%2Fnote.youdao.com%2Fyws%2Fres%2F42223%2F3259B40DE0D0411984701E672AEB9942"></p><p data-source-line="184">解析:</p><p data-source-line="186">当new出一个线程时,其实线程并没有工作。它只是生成了一个实体,当你调用这个实例的start方法时,线程才真正地被启动。启动后到Runnable状态,Runnable表示该线程的资源等等已经被准备好,已经可以执行了,但是并不表示一定在执行状态,由于时间片轮转,该线程也可能此时并没有在执行。对于我们来说,该线程可以认为已经被执行了,但是是否真实执行,还得看物理cpu的调度。当线程任务执行结束后,线程就到了Terminated状态。</p><p data-source-line="188">有时候在线程的执行当中,不可避免的会申请某些锁或某个对象的监视器,当无法获取时,这个线程会被阻塞住,会被挂起,到了Blocked状态。如果这个线程调用了wait方法,它就处于一个Waiting状态。进入Waiting状态的线程会等待其他线程给它notify,通知到之后由Waiting状态又切换到Runnable状态继续执行。当然等待状态有两种,一种是无限期等待,直到被notify。一直则是有限期等待,比如等待10秒还是没有被notify,则自动切换到Runnable状态。</p><p data-source-line="190">Thread的类中定义的线程状态枚举类:</p><pre data-source-line="192"><code class="hljs">public enum State {
/**
* Thread <span class="hljs-keyword">state</span> <span class="hljs-keyword">for</span> a thread which has not yet started.
*/
NEW,
/**
* Thread <span class="hljs-keyword">state</span> <span class="hljs-keyword">for</span> a runnable thread. A thread <span class="hljs-keyword">in</span> the runnable
* <span class="hljs-keyword">state</span> is executing <span class="hljs-keyword">in</span> the Java virtual machine but it may
* be waiting <span class="hljs-keyword">for</span> other resources <span class="hljs-keyword">from</span> the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread <span class="hljs-keyword">state</span> <span class="hljs-keyword">for</span> a thread blocked waiting <span class="hljs-keyword">for</span> a monitor lock.
* A thread <span class="hljs-keyword">in</span> the blocked <span class="hljs-keyword">state</span> is waiting <span class="hljs-keyword">for</span> a monitor lock
* <span class="hljs-keyword">to</span> enter a synchronized <span class="hljs-built_in">block</span>/method or
* reenter a synchronized <span class="hljs-built_in">block</span>/method after calling
* {@link Object<span class="hljs-comment">#wait() Object.wait}.</span>
*/
BLOCKED,
/**
* Thread <span class="hljs-keyword">state</span> <span class="hljs-keyword">for</span> a waiting thread.
* A thread is <span class="hljs-keyword">in</span> the waiting <span class="hljs-keyword">state</span> due <span class="hljs-keyword">to</span> calling one of the
* following methods:
* <span class="hljs-variable"><ul></span>
* <span class="hljs-variable"><li></span>{@link Object<span class="hljs-comment">#wait() Object.wait} with no timeout</li></span>
* <span class="hljs-variable"><li></span>{@link <span class="hljs-comment">#join() Thread.join} with no timeout</li></span>
* <span class="hljs-variable"><li></span>{@link LockSupport<span class="hljs-comment">#park() LockSupport.park}</li></span>
* </ul>
*
* <span class="hljs-variable"><p></span>A thread <span class="hljs-keyword">in</span> the waiting <span class="hljs-keyword">state</span> is waiting <span class="hljs-keyword">for</span> another thread <span class="hljs-keyword">to</span>
* perform a particular action.
*
* For example, a thread that has called <span class="hljs-variable"><tt></span>Object.wait()</tt>
* <span class="hljs-keyword">on</span> an object is waiting <span class="hljs-keyword">for</span> another thread <span class="hljs-keyword">to</span> call
* <span class="hljs-variable"><tt></span>Object.notify()</tt> or <span class="hljs-variable"><tt></span>Object.notifyAll()</tt> <span class="hljs-keyword">on</span>
* that object. A thread that has called <span class="hljs-variable"><tt></span>Thread.join()</tt>
* is waiting <span class="hljs-keyword">for</span> a specified thread <span class="hljs-keyword">to</span> terminate.
*/
WAITING,
/**
* Thread <span class="hljs-keyword">state</span> <span class="hljs-keyword">for</span> a waiting thread with a specified waiting time.
* A thread is <span class="hljs-keyword">in</span> the timed waiting <span class="hljs-keyword">state</span> due <span class="hljs-keyword">to</span> calling one of
* the following methods with a specified positive waiting time:
* <span class="hljs-variable"><ul></span>
* <span class="hljs-variable"><li></span>{@link <span class="hljs-comment">#sleep Thread.sleep}</li></span>
* <span class="hljs-variable"><li></span>{@link Object<span class="hljs-comment">#wait(long) Object.wait} with timeout</li></span>
* <span class="hljs-variable"><li></span>{@link <span class="hljs-comment">#join(long) Thread.join} with timeout</li></span>
* <span class="hljs-variable"><li></span>{@link LockSupport<span class="hljs-comment">#parkNanos LockSupport.parkNanos}</li></span>
* <span class="hljs-variable"><li></span>{@link LockSupport<span class="hljs-comment">#parkUntil LockSupport.parkUntil}</li></span>
* </ul>
*/
TIMED_WAITING,
/**
* Thread <span class="hljs-keyword">state</span> <span class="hljs-keyword">for</span> a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
</code></pre><h2 data-source-line="259" id="blocked-状态可能的原因"><a class="markdownIt-Anchor" href="#blocked-状态可能的原因"></a>BLOCKED 状态可能的原因:</h2><ol data-source-line="261"><li>通过调用sleep(milliseconds)。</li><li>通过调用wait().</li><li>任务在等待某个任务的输入或输出完成。</li><li>任务试图在<mark>某个对象上</mark>调用其同步控制方法,但是另一个任务已经获得该锁时。</li></ol><h2 data-source-line="266" id="blocked-与-waiting-的区别"><a class="markdownIt-Anchor" href="#blocked-与-waiting-的区别"></a>BLOCKED 与 WAITING 的区别</h2><ul data-source-line="268"><li>BLOCKED 是指线程正在等待获取锁;</li><li>WAITING 是指线程正在等待其他线程发来的通知(notify),收到通知后,可能会顺序向后执行(RUNNABLE),也可能会再次获取锁,进而被阻塞住(BLOCKED)。</li></ul><h1 data-source-line="271" id="java线程的其他知识"><a class="markdownIt-Anchor" href="#java线程的其他知识"></a>Java线程的其他知识</h1><h3 data-source-line="273" id="runable-thread"><a class="markdownIt-Anchor" href="#runable-thread"></a>Runable、Thread</h3><ul data-source-line="275"><li>Runable是一个接口,相当于对任务的一种描述(在run()内描述任务)。</li><li>Thread是一个类。将任务附着到线程上,从而实现线程的行为。</li></ul><h3 data-source-line="279" id="线程基本操作"><a class="markdownIt-Anchor" href="#线程基本操作"></a>线程基本操作</h3><h4 data-source-line="282" id="新建线程-threadstart"><a class="markdownIt-Anchor" href="#新建线程-threadstart"></a>新建线程-Thread.start()</h4><p data-source-line="283">start方法其实是在一个新的操作系统线程上面去调用run方法。换句话说,<mark>直接调用run方法而不是调用start方法</mark>的话,它并不会开启新的线程,而是在调用run的当前的线程当中执行你的操作。</p><pre data-source-line="286"><code class="hljs"><span class="hljs-comment">//<span class="zh-hans">输出</span>t1</span>
<span class="hljs-keyword">Thread</span> <span class="hljs-keyword">thread</span> = <span class="hljs-literal">new</span> <span class="hljs-keyword">Thread</span>(<span class="hljs-string">"t1"</span>)
{
@Override
<span class="hljs-keyword">public</span> <span class="hljs-literal">void</span> run()
{
<span class="hljs-comment">// TODO Auto-generated method stub</span>
System.out.println(<span class="hljs-keyword">Thread</span>.currentThread().getName());
}
};
<span class="hljs-keyword">thread</span>.start();
</code></pre><pre data-source-line="301"><code class="hljs"><span class="hljs-comment">//<span class="zh-hans">输出</span>main<span class="zh-hans">,直接调用</span>run<span class="zh-hans">其实就是一个普通的函数调用而已,并没有达到多线程的作用</span></span>
<span class="hljs-keyword">Thread</span> <span class="hljs-keyword">thread</span> = <span class="hljs-literal">new</span> <span class="hljs-keyword">Thread</span>(<span class="hljs-string">"t1"</span>)
{
@Override
<span class="hljs-keyword">public</span> <span class="hljs-literal">void</span> run()
{
<span class="hljs-comment">// TODO Auto-generated method stub</span>
System.out.println(<span class="hljs-keyword">Thread</span>.currentThread().getName());
}
};
<span class="hljs-keyword">thread</span>.run();
</code></pre><p data-source-line="315">run方法的实现有两种方式。
其一就是上面的方法,还有一种是</p><pre data-source-line="318"><code class="hljs"><span class="hljs-keyword">Thread</span> t1=<span class="hljs-keyword">new</span> <span class="hljs-keyword">Thread</span>(<span class="hljs-keyword">new</span> CreateThread3())<span class="hljs-comment">;</span>
</code></pre><p data-source-line="321">此种方法显然显得更加面向对象。</p><h4 data-source-line="323" id="终止线程-threadstop"><a class="markdownIt-Anchor" href="#终止线程-threadstop"></a>终止线程-<s>Thread.stop()</s></h4><blockquote data-source-line="325"><p>Thread.stop() 不推荐使用。它会释放所有monitor</p></blockquote><p data-source-line="327">原因在于stop方法太过"暴力"了,无论线程执行到哪里,它将会立即停止掉线程。</p><p data-source-line="329"><img data-src="http%3A%2F%2Fstatic.oschina.net%2Fuploads%2Fspace%2F2016%2F0107%2F113702_iwlY_2243330.jpg" alt="link" src="http://static.oschina.net/uploads/space/2016/0107/113702_iwlY_2243330.jpg?ynotemdtimestamp=1562307580979" data-processed="http%3A%2F%2Fstatic.oschina.net%2Fuploads%2Fspace%2F2016%2F0107%2F113702_iwlY_2243330.jpg"></p><p data-source-line="331">当写线程得到锁以后开始写入数据,写完id = 1,在准备将name = 1时被stop,释放锁。读线程获得锁进行读操作,读到的id为1,而name还是0,导致了数据不一致。</p><p data-source-line="333"><mark>最重要的是这种错误不会抛出异常,将很难被发现。</mark></p><p data-source-line="335">既然stop()会破坏原子逻辑,那么我们可以自定义一个volatile变量,自己去实现一个终止线程的方法。</p><pre data-source-line="337"><code class="hljs"><span class="hljs-keyword">package</span> <span class="zh-hans">线程基础</span>;
<span class="hljs-comment">/**
* Description:volatile<span class="zh-hans">具备可见性</span>
* <p>
* 1. <span class="zh-hans">在设置成</span>false<span class="zh-hans">之后,线程依然没有关闭,原因是只改变了本线程的</span>isRunning<span class="zh-hans">变量的副本,而没有改变全局变量的值</span>.<br>
* 2. <span class="zh-hans">可以加上</span>volatile<span class="zh-hans">关键字设置该变量为其他线程可见。</span>
*
* <span class="hljs-doctag">@author</span> SunYukun
* <span class="hljs-doctag">@date</span> 2017/7/2
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Volatile_01</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span> </span>{
<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">boolean</span> isRunning = <span class="hljs-keyword">true</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{
Volatile_01 thread = <span class="hljs-keyword">new</span> Volatile_01();
thread.start();
Thread.sleep(<span class="hljs-number">1000</span>);
thread.setIsRunning(<span class="hljs-keyword">false</span>);
System.out.println(<span class="hljs-string">"<span class="zh-hans">设置成</span>false"</span>);
}
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setIsRunning</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span> flag)</span> </span>{
isRunning = flag;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">while</span> (isRunning) {
System.out.println(<span class="hljs-string">"running ...."</span>);
}
System.out.println(<span class="hljs-string">"stop"</span>);
}
}
</code></pre><h4 data-source-line="379" id="线程中断-threadinterrupt"><a class="markdownIt-Anchor" href="#线程中断-threadinterrupt"></a>线程中断-Thread.interrupt</h4><p data-source-line="381">Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在<mark>合适的时机中断自己</mark>。</p><p data-source-line="383">每个线程都有一个boolean的中断状态(<mark>不一定就是对象的属性,事实上,该状态也确实不是Thread的字段</mark>),interrupt方法仅仅只是将该状态置为true。对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true,<mark>并不会使程序停止</mark>;</p><pre data-source-line="385"><code class="hljs"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>)</span>{<span class="hljs-comment">//<span class="zh-hans">线程</span>t1</span>
<span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>){
Thread.<span class="hljs-keyword">yield</span>();
}
}
t1.interrupt();
</code></pre><p data-source-line="393">这样使线程t1中断,是不会有效果的,只是更改了中断状态位。</p><p data-source-line="395">如果希望非常优雅地终止这个线程,就该这样做:</p><pre data-source-line="397"><code class="hljs"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>)</span>{
<span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>)
{
<span class="hljs-keyword">if</span>(Thread.currentThread().isInterrupted())
{
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Interruted!"</span>);
<span class="hljs-keyword">break</span>;
}
Thread.<span class="hljs-keyword">yield</span>();
}
}
</code></pre><p data-source-line="410"><ins>使用中断,就对数据一致性有了一定的保证。</ins></p><p data-source-line="412">对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会<mark>抛出InterruptedException</mark>, 同时会<mark>把中断状态置回为false.</mark></p><p data-source-line="414">对于取消阻塞状态中的线程,可以这样书写代码:</p><pre data-source-line="416"><code class="hljs"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>)</span>{
<span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>){
<span class="hljs-keyword">if</span>(Thread.currentThread().isInterrupted()){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Interrupted!"</span>);
<span class="hljs-keyword">break</span>;
}
<span class="hljs-keyword">try</span> {
Thread.sleep(<span class="hljs-number">2000</span>);
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Interruted When Sleep"</span>);
<span class="hljs-comment">//<span class="zh-hans">设置中断状态,抛出异常后会清除中断标记位</span></span>
Thread.currentThread().interrupt();
}
Thread.<span class="hljs-keyword">yield</span>();
}
}
</code></pre><h4 data-source-line="435" id="等待与通知waitnotify"><a class="markdownIt-Anchor" href="#等待与通知waitnotify"></a>等待与通知——wait()/notify()</h4><ol data-source-line="437"><li>属于Object类的方法;</li><li>wait()是释放锁的;</li><li><mark>notify()不释放锁;</mark></li><li>需要配合synchronized关键字使用。</li></ol><pre data-source-line="442"><code class="hljs"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">wait</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>{
wait(<span class="hljs-number">0</span>);
}
</code></pre><pre data-source-line="448"><code class="hljs"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">native</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">notify</span><span class="hljs-params">()</span></span>;
</code></pre><p data-source-line="452">如果在一个线程上调用一个对象的object.wait(),那么这个线程就会进入object对象的等待队列。</p><p data-source-line="454">调用notify()会随机的唤醒一个等待队列中的线程。</p><h4 data-source-line="459" id="线程挂起-threadsuspend"><a class="markdownIt-Anchor" href="#线程挂起-threadsuspend"></a>线程挂起-<s>Thread.suspend()</s></h4><p data-source-line="460">挂起(suspend)和继续执行(resume)线程</p><p data-source-line="462">suspend()<mark>不会释放锁</mark>
如果加锁发生在resume()之前 ,则死锁发生
这两个方法都是Deprecated方法,不推荐使用。</p><p data-source-line="466">原因在于,suspend不释放锁,因此没有线程可以访问被它锁住的临界区资源,直到被其他线程resume。</p><p data-source-line="468">因为无法控制线程运行的先后顺序,如果其他线程的resume方法先被运行,那则后运行的suspend,将一直占有这把锁,造成死锁发生。</p><p data-source-line="470">用以下代码来模拟这个场景</p><pre data-source-line="473"><code class="hljs"><span class="hljs-keyword">package</span> test;
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span>
</span>{
<span class="hljs-keyword">static</span> Object u = <span class="hljs-keyword">new</span> Object();
<span class="hljs-keyword">static</span> TestSuspendThread t1 = <span class="hljs-keyword">new</span> TestSuspendThread(<span class="hljs-string">"t1"</span>);
<span class="hljs-keyword">static</span> TestSuspendThread t2 = <span class="hljs-keyword">new</span> TestSuspendThread(<span class="hljs-string">"t2"</span>);
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestSuspendThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span>
</span>{
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">TestSuspendThread</span><span class="hljs-params">(String name)</span>
</span>{
setName(name);
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span>
</span>{
<span class="hljs-keyword">synchronized</span> (u)
{
System.out.println(<span class="hljs-string">"in "</span> + getName());
Thread.currentThread().suspend();
}
}
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException
</span>{
t1.start();
Thread.sleep(<span class="hljs-number">100</span>);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
</code></pre><p data-source-line="512">让t1,t2同时争夺一把锁,争夺到的线程suspend,然后再resume,按理来说,应该某个线程争夺后被resume释放了锁,然后另一个线程争夺掉锁,再被resume。
结果输出是:</p><pre data-source-line="515"><code class="hljs"><span class="hljs-keyword">in</span> t1
<span class="hljs-keyword">in</span> t2
</code></pre><p data-source-line="519">说明两个线程都争夺到了锁,但是控制台的红灯还是亮着的,说明t1,t2一定有线程没有执行完。我们dump出堆来看看
<img data-src="http%3A%2F%2Fstatic.oschina.net%2Fuploads%2Fspace%2F2016%2F0107%2F150701_t6gX_2243330.jpg" alt="image" src="http://static.oschina.net/uploads/space/2016/0107/150701_t6gX_2243330.jpg?ynotemdtimestamp=1562307580979" data-processed="http%3A%2F%2Fstatic.oschina.net%2Fuploads%2Fspace%2F2016%2F0107%2F150701_t6gX_2243330.jpg"></p><h4 data-source-line="522" id="join和yeild"><a class="markdownIt-Anchor" href="#join和yeild"></a>join和yeild</h4><p data-source-line="524">yeild是个native静态方法,这个方法是想把自己占有的cpu时间<mark>释放掉,然后和其他线程一起竞争</mark>(注意yeild的线程还是有可能争夺到cpu,注意与sleep区别)。在javadoc中也说明了,yeild是个基本不会用到的方法,一般在debug和test中使用。</p><p data-source-line="526">join方法的意思是<mark>等待其他线程结束,就如suspend那节的代码,想让主线程等待t1,t2结束以后再结束。没有结束的话,主线程就一直阻塞在那里</mark>。按我的理解就是,调用此方法,就是给t1,t2加入到该主线程中,也就是主线程得等到他们都结束了,才能结束自己的任务。</p><pre data-source-line="529"><code class="hljs"><span class="hljs-keyword">package</span> test;
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span>
</span>{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span>
</span>{
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span>
</span>{
<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10000000</span>; i++)
;
}
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException
</span>{
AddThread at = <span class="hljs-keyword">new</span> AddThread();
at.start();
at.join();
System.out.println(i);
}
}
</code></pre><p data-source-line="555">如果把上述代码的at.join去掉,则主线程会直接运行结束,i的值会很小。如果有join,打印出的i的值一定是10000000。
那么join是怎么实现的呢?</p><p data-source-line="558">join的本质 :</p><pre data-source-line="561"><code class="hljs"><span class="hljs-keyword">while</span> <span class="hljs-comment">(isAlive()</span>) {<span class="hljs-comment">//<span class="zh-hans">意为无限期的等待</span></span>
wait<span class="hljs-comment">(0)</span>;
}
</code></pre><p data-source-line="566">join()方法也可以传递一个时间,意为有限期地等待,超过了这个时间就自动唤醒。</p><p data-source-line="568">这样就有一个问题,谁来notify这个线程呢,在thread类中没有地方调用了notify?</p><p data-source-line="570">在javadoc中,找到了相关解释。当一个线程运行完成终止后,将会<mark>调用notifyAll方法去唤醒等待在当前线程实例上的所有线程,这个操作是jvm自己完成的</mark>。</p><p data-source-line="572">所以javadoc中还给了我们一个建议,<mark>不要使用wait和notify/notifyall在线程实例上</mark>。因为jvm会自己调用,有可能与你调用期望的结果不同。</p><h4 data-source-line="574" id="守护线程-threadsetdeamontrue"><a class="markdownIt-Anchor" href="#守护线程-threadsetdeamontrue"></a>守护线程-Thread.setDeamon(true)</h4><blockquote data-source-line="576"><p>Daemon 中的 ae 为一个音,重音在 /di:/ 上,好象是指希腊守护神的意思吧。在计算机专业英语中是守护线程的意思。<mark>原以为</mark>如果一个线程被光荣地定义为守护线程,一定会直到进程运行到最后一刻,但真错了,如果一个线程是守护线程,那么,主线程运行结束时,如果没有任何非守护线程在运行,守护线程就会自尽了。</p></blockquote><blockquote data-source-line="578"><p>setDaemon方法是Thread中的方法,默认为false状态,可将该线程标记为守护线程或用户线程,该方法必须在启动线程前调用,具有最低的优先级,让系统资源优先调用其他线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。</p></blockquote><ol data-source-line="580"><li>在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。</li><li><strong>当一个Java应用内,所有非守护进程都结束时,Java虚拟机就会自然退出。</strong></li><li>setDaemon需要在start方法调用之前使用。</li><li>主线程结束后:
1) 用户线程将会继续运行
2) 如果没有用户线程,都是后台进程的话,那么jvm结束。</li></ol><p data-source-line="588">这样就开启了一个守护线程:</p><pre data-source-line="590"><code class="hljs"><span class="hljs-type">Thread</span> t=<span class="hljs-function"><span class="hljs-keyword">new</span> <span class="hljs-title">DaemonT</span>();
<span class="hljs-title">t</span>.<span class="hljs-title">setDaemon</span>(true);
<span class="hljs-title">t</span>.<span class="hljs-title">start</span>();
</span></code></pre><pre data-source-line="597"><code class="hljs"><span class="hljs-keyword">package</span> test;
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span>
</span>{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DaemonThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span>
</span>{
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span>
</span>{
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10000000</span>; i++)
{
System.out.println(<span class="hljs-string">"hi"</span>);
}
}
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException
</span>{
DaemonThread dt = <span class="hljs-keyword">new</span> DaemonThread();
dt.start();
}
}
</code></pre><p data-source-line="621">当线程dt不是一个守护线程时,在运行后,我们能看到控制台输出hi
当在start之前加入</p><pre data-source-line="624"><code class="hljs">dt.setDaemon(true)<span class="hljs-comment">;</span>
</code></pre><p data-source-line="627">控制台就直接退出了,并没有输出。</p><h3 data-source-line="637" id="线程安全-与-线程不安全"><a class="markdownIt-Anchor" href="#线程安全-与-线程不安全"></a>线程安全 与 线程不安全</h3><p data-source-line="638">线程安全</p><blockquote data-source-line="640"><p>就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。</p></blockquote><p data-source-line="642">线程不安全</p><blockquote data-source-line="643"><p>就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据</p></blockquote><h3 data-source-line="645" id="进程间通信"><a class="markdownIt-Anchor" href="#进程间通信"></a>进程间通信</h3><h4 data-source-line="647" id="管道-pipe"><a class="markdownIt-Anchor" href="#管道-pipe"></a>管道( pipe )</h4><p data-source-line="649">管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。</p><p data-source-line="651">有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。</p><h4 data-source-line="653" id="信号量-semophore"><a class="markdownIt-Anchor" href="#信号量-semophore"></a>信号量( semophore )</h4><p data-source-line="654">信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。</p><h4 data-source-line="657" id="消息队列-message-queue"><a class="markdownIt-Anchor" href="#消息队列-message-queue"></a>消息队列( message queue )</h4><p data-source-line="658">消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。</p><h4 data-source-line="661" id="信号-sinal"><a class="markdownIt-Anchor" href="#信号-sinal"></a>信号 ( sinal )</h4><p data-source-line="662">信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。</p><h4 data-source-line="664" id="共享内存-shared-memory"><a class="markdownIt-Anchor" href="#共享内存-shared-memory"></a>共享内存( shared memory )</h4><p data-source-line="665">共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。</p><h4 data-source-line="667" id="套接字-socket"><a class="markdownIt-Anchor" href="#套接字-socket"></a>套接字( socket )</h4><p data-source-line="668">套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。</p><h3 data-source-line="671" id="线程间通信"><a class="markdownIt-Anchor" href="#线程间通信"></a>线程间通信</h3><p data-source-line="673">线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。</p><p data-source-line="675">Object类中相关的方法有两个notify方法和三个wait方法:</p><p data-source-line="677"><a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html" target="_blank">http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html</a></p><p data-source-line="679">因为wait和notify方法定义在Object类中,因此会被所有的类所继承。</p><p data-source-line="681"><mark>这些方法都是final的</mark>,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。</p><h4 data-source-line="683" id="1-通过共享对象通信"><a class="markdownIt-Anchor" href="#1-通过共享对象通信"></a>1、通过共享对象通信</h4><p data-source-line="685">线程间发送信号的一个简单方式是在共享对象的变量里设置信号值。线程A在一个同步块里设置boolean型成员变量hasDataToProcess为true,线程B也在同步块里读取hasDataToProcess这个成员变量。这个简单的例子使用了一个持有信号的对象,并提供了set和check方法:</p><pre data-source-line="687"><code class="hljs"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MySignal</span></span>{
<span class="hljs-keyword">protected</span> <span class="hljs-keyword">boolean</span> hasDataToProcess = <span class="hljs-keyword">false</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">hasDataToProcess</span><span class="hljs-params">()</span></span>{
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.hasDataToProcess;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setHasDataToProcess</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span> hasData)</span></span>{
<span class="hljs-keyword">this</span>.hasDataToProcess = hasData;
}
}
</code></pre><p data-source-line="703">线程A和B必须获得指向一个MySignal共享实例的引用,以便进行通信。如果它们持有的引用指向不同的MySingal实例,那么彼此将不能检测到对方的信号。需要处理的数据可以存放在一个共享缓存区里,它和MySignal实例是分开存放的。</p><h4 data-source-line="704" id="2-忙等待"><a class="markdownIt-Anchor" href="#2-忙等待"></a>2、忙等待</h4><h4 data-source-line="706" id="3-waitnotify和notifyall"><a class="markdownIt-Anchor" href="#3-waitnotify和notifyall"></a>3、wait(),notify()和notifyAll()</h4><p data-source-line="708">在一个对象上调用wait()和notify()时,这个object对象也就成了多个线程之间的一个通信手段。</p><h5 data-source-line="710" id="例-1通知其他线程"><a class="markdownIt-Anchor" href="#例-1通知其他线程"></a>例 1:通知其他线程</h5><ul data-source-line="712"><li><ol><li>t1线程去添加list,希望加够5个之后,让t2线程感知到。</li></ol></li><li><ol start="2"><li>但是不好的地方是,t2线程是一直启动着的,一直在<mark>轮询</mark>。</li></ol></li></ul><pre data-source-line="715"><code class="hljs"><span class="hljs-keyword">public</span> class MyThread_01 {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">static</span> List<<span class="hljs-keyword">String</span>> list = <span class="hljs-keyword">new</span> ArrayList<<span class="hljs-keyword">String</span>>();
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-keyword">String</span>[] args) {
<span class="hljs-keyword">final</span> MyThread_02 t = <span class="hljs-keyword">new</span> MyThread_02();
<span class="hljs-comment">//<span class="zh-hans">线程</span>1 <span class="zh-hans">往</span>list<span class="zh-hans">中加数据</span></span>
Thread t1 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) {
t.<span class="hljs-built_in">add</span>();
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">当前线程</span>"</span> + Thread.currentThread().getName() + <span class="hljs-string">":<span class="zh-hans">添加了一个数据。。</span>"</span>);
Thread.sleep(<span class="hljs-number">500</span>);
}
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
e.printStackTrace();
}
}
}, <span class="hljs-string">"t1"</span>);
<span class="hljs-comment">//<span class="zh-hans">线程</span>2 <span class="zh-hans">当</span>list<span class="zh-hans">数组中有</span>5<span class="zh-hans">个元素时</span>,<span class="zh-hans">就打印一句话,表示自己收到了通知</span></span>
Thread t2 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) {
<span class="hljs-keyword">if</span> (t.<span class="hljs-built_in">size</span>() == <span class="hljs-number">5</span>) {
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">当前线程</span>"</span> + Thread.currentThread().getName() + <span class="hljs-string">":<span class="zh-hans">接收到通知。。并停止线程</span>"</span>);
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException();
}
}
<span class="hljs-comment">//<span class="zh-hans">打印一句话</span></span>
}
}, <span class="hljs-string">"t2"</span>);
t1.start();
t2.start();
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-built_in">add</span>() {
list.<span class="hljs-built_in">add</span>(<span class="hljs-string">"1"</span>);
}
<span class="hljs-keyword">public</span> <span class="hljs-built_in">int</span> <span class="hljs-built_in">size</span>() {
<span class="hljs-keyword">return</span> list.<span class="hljs-built_in">size</span>();
}
}
</code></pre><h5 data-source-line="764" id="例-2使用wait-notify通知"><a class="markdownIt-Anchor" href="#例-2使用wait-notify通知"></a>例 2:使用wait()、notify()通知</h5><ul data-source-line="766"><li><ol><li>并未实时通知对方;因为t1拿到线程之后不释放锁,所以非得等到它执行完以后,才有机会执行t2,到那时也就不等于5了。</li></ol></li><li><ol start="2"><li>wait()会阻塞。</li></ol></li></ul><pre data-source-line="769"><code class="hljs"><span class="hljs-keyword">public</span> class MyThread_02 {
<span class="hljs-comment">//<span class="zh-hans">声明一个</span> <span class="zh-hans">实例变量</span></span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">static</span> List<<span class="hljs-keyword">String</span>> list = <span class="hljs-keyword">new</span> ArrayList<<span class="hljs-keyword">String</span>>();
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-keyword">String</span>[] args) {
<span class="hljs-keyword">final</span> MyThread_02 t = <span class="hljs-keyword">new</span> MyThread_02();
<span class="hljs-comment">//<span class="zh-hans">全局对象锁</span></span>
<span class="hljs-keyword">final</span> <span class="hljs-keyword">Object</span> lock = <span class="hljs-keyword">new</span> <span class="hljs-keyword">Object</span>();
<span class="hljs-comment">//<span class="zh-hans">线程</span>1 <span class="zh-hans">往</span>list<span class="zh-hans">中加数据</span></span>
Thread t1 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">synchronized</span> (lock) {<span class="hljs-comment">//<span class="zh-hans">说明会对整个这块代码块加上锁。</span></span>
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) {
t.<span class="hljs-built_in">add</span>();
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">当前线程</span>"</span> + Thread.currentThread().getName() + <span class="hljs-string">":<span class="zh-hans">添加了一个数据。。</span>"</span>);
Thread.sleep(<span class="hljs-number">500</span>);
<span class="hljs-keyword">if</span> (list.<span class="hljs-built_in">size</span>() == <span class="hljs-number">5</span>) {
<span class="hljs-comment">//<span class="zh-hans">发送通知</span> <span class="zh-hans">但是</span>notifi<span class="zh-hans">方法不释放锁</span> <span class="zh-hans">锁被释放</span> <span class="zh-hans">是</span>10<span class="zh-hans">次循环结束后释放</span></span>
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">已经发出通知。。</span>"</span>);
lock.notify();
}
}
}
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
e.printStackTrace();
}
}
}, <span class="hljs-string">"t1"</span>);
<span class="hljs-comment">//<span class="zh-hans">线程</span>2 <span class="zh-hans">当</span>list<span class="zh-hans">数组中有</span>5<span class="zh-hans">个元素时</span>,<span class="zh-hans">就打印一句话,表示自己收到了通知</span></span>
Thread t2 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {
<span class="hljs-keyword">synchronized</span> (lock) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">if</span> (t.<span class="hljs-built_in">size</span>() != <span class="hljs-number">5</span>) {
<span class="hljs-comment">//wait<span class="zh-hans">释放锁</span> <span class="zh-hans">此时的线程就在这儿阻塞</span> <span class="zh-hans">所以下面的</span>print<span class="zh-hans">语句没有打印</span> <span class="zh-hans">相反的</span>t1<span class="zh-hans">线程获得了锁</span> <span class="zh-hans">也发出了通知</span></span>
<span class="hljs-comment">//<span class="zh-hans">这时</span> <span class="zh-hans">线程</span>2<span class="zh-hans">就从阻塞状态中到</span>active<span class="zh-hans">状态</span> <span class="zh-hans">于是就有了下面的打印</span> <span class="zh-hans">线程阻塞时</span> <span class="zh-hans">就让出了</span>cpu</span>
lock.wait();
}
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
e.printStackTrace();
}
<span class="hljs-comment">//<span class="zh-hans">打印一句话</span></span>
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">当前线程</span>"</span> + Thread.currentThread().getName() + <span class="hljs-string">":<span class="zh-hans">接收到通知。。</span>"</span>);
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException();
}
}
}, <span class="hljs-string">"t2"</span>);
<span class="hljs-comment">//<span class="zh-hans">注意肯定</span>t2<span class="zh-hans">先启动,若是后启动的话,因为</span>notify<span class="zh-hans">不释放锁,所以会造成</span>t2<span class="zh-hans">不会执行。</span></span>
t2.start();
t1.start();
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-built_in">add</span>() {
list.<span class="hljs-built_in">add</span>(<span class="hljs-string">"1"</span>);
}
<span class="hljs-keyword">public</span> <span class="hljs-built_in">int</span> <span class="hljs-built_in">size</span>() {
<span class="hljs-keyword">return</span> list.<span class="hljs-built_in">size</span>();
}
}
</code></pre><h5 data-source-line="839" id="例-3利用countdownlatch类实现"><a class="markdownIt-Anchor" href="#例-3利用countdownlatch类实现"></a>例 3:利用CountDownLatch类实现</h5><ol data-source-line="841"><li>实时通知。</li></ol><pre data-source-line="843"><code class="hljs"><span class="hljs-keyword">public</span> class MyThread_03 {
<span class="hljs-comment">//<span class="zh-hans">声明一个</span> <span class="zh-hans">实例变量</span></span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">static</span> List<<span class="hljs-keyword">String</span>> list = <span class="hljs-keyword">new</span> ArrayList<<span class="hljs-keyword">String</span>>();
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-keyword">String</span>[] args) {
<span class="hljs-keyword">final</span> MyThread_03 t = <span class="hljs-keyword">new</span> MyThread_03();
<span class="hljs-comment">//<span class="zh-hans">只需要执行</span>1<span class="zh-hans">次</span>c.countDown();</span>
<span class="hljs-keyword">final</span> CountDownLatch c = <span class="hljs-keyword">new</span> CountDownLatch(<span class="hljs-number">1</span>);
<span class="hljs-comment">//<span class="zh-hans">线程</span>1 <span class="zh-hans">往</span>list<span class="zh-hans">中加数据</span></span>
Thread t1 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) {
t.<span class="hljs-built_in">add</span>();
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">当前线程</span>"</span> + Thread.currentThread().getName() + <span class="hljs-string">":<span class="zh-hans">添加了一个数据。。</span>"</span>);
Thread.sleep(<span class="hljs-number">500</span>);
<span class="hljs-keyword">if</span> (list.<span class="hljs-built_in">size</span>() == <span class="hljs-number">5</span>) {
<span class="hljs-comment">//<span class="zh-hans">发送通知</span> <span class="zh-hans">但是</span>notifi<span class="zh-hans">方法不释放锁</span> <span class="zh-hans">锁被释放是在</span>10<span class="zh-hans">次循环结束后释放</span>(<span class="zh-hans">即</span>synchronized<span class="zh-hans">块结束后</span>)</span>
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">已经发出通知。。</span>"</span>);
c.countDown();
}
}
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
e.printStackTrace();
}
}
}, <span class="hljs-string">"t1"</span>);
<span class="hljs-comment">//<span class="zh-hans">线程</span>2 <span class="zh-hans">当</span>list<span class="zh-hans">数组中有</span>5<span class="zh-hans">个元素时</span> <span class="zh-hans">就打印一句话</span></span>
Thread t2 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">if</span> (t.<span class="hljs-built_in">size</span>() != <span class="hljs-number">5</span>) {
c.await();
}
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
e.printStackTrace();
}
<span class="hljs-comment">//<span class="zh-hans">打印一句话</span></span>
System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"<span class="zh-hans">当前线程</span>"</span> + Thread.currentThread().getName() + <span class="hljs-string">":<span class="zh-hans">接收到通知。。</span>"</span>);
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException();
}
<span class="hljs-comment">//}</span>
}, <span class="hljs-string">"t2"</span>);
t2.start();
t1.start();
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-built_in">add</span>() {
list.<span class="hljs-built_in">add</span>(<span class="hljs-string">"1"</span>);
}
<span class="hljs-keyword">public</span> <span class="hljs-built_in">int</span> <span class="hljs-built_in">size</span>() {
<span class="hljs-keyword">return</span> list.<span class="hljs-built_in">size</span>();
}
}
</code></pre><h4 data-source-line="916" id="4-丢失的信号"><a class="markdownIt-Anchor" href="#4-丢失的信号"></a>4、丢失的信号</h4><h4 data-source-line="918" id="5-假唤醒"><a class="markdownIt-Anchor" href="#5-假唤醒"></a>5、假唤醒</h4><h4 data-source-line="920" id="6-多线程等待相同信号"><a class="markdownIt-Anchor" href="#6-多线程等待相同信号"></a>6、多线程等待相同信号</h4><h4 data-source-line="922" id="7-不要对常量字符串或全局对象调用wait"><a class="markdownIt-Anchor" href="#7-不要对常量字符串或全局对象调用wait"></a>7、不要对常量字符串或全局对象调用wait()</h4><h2 data-source-line="930" id="同步和异步"><a class="markdownIt-Anchor" href="#同步和异步"></a>同步和异步:</h2><h2 data-source-line="932" id="临界区"><a class="markdownIt-Anchor" href="#临界区"></a>临界区:</h2><p data-source-line="934">临界区表示一种公共资源,在并行程序中,临界区资源时需要保护的。</p><h2 data-source-line="946" id="死锁-饥饿-活锁"><a class="markdownIt-Anchor" href="#死锁-饥饿-活锁"></a>死锁、饥饿、活锁:</h2><h3 data-source-line="948" id="死锁"><a class="markdownIt-Anchor" href="#死锁"></a>死锁</h3><p data-source-line="951">有个著名的例子——“哲学家进餐”问题,可以形象的表述死锁问题。</p><blockquote data-source-line="952"><p>5个哲学家围坐在一个圆桌上,每两个哲学家之间都有一只筷子,也就是只有5支筷子,哲学家平时进行思考,只有当他们饥饿时,才拿起筷子吃饭。</p></blockquote><p data-source-line="954">代码实现:</p><pre data-source-line="956"><code class="hljs"><span class="hljs-keyword">package</span> <span class="zh-hans">线程基础</span>;
<span class="hljs-keyword">import</span> java.util.Random;
<span class="hljs-keyword">import</span> java.util.concurrent.ExecutorService;
<span class="hljs-keyword">import</span> java.util.concurrent.Executors;
<span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;
<span class="hljs-comment">/**
* Author: sun.yukun@foxmail.com
* Time: 2017/9/17 <span class="zh-hans">下午</span>8:33
* Blog: coderdaily.net
* <p>
* <span class="zh-hans">哲学家进餐问题</span>-<span class="zh-hans">模拟死锁</span>
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Philosopher</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>{
<span class="hljs-comment">// <span class="zh-hans">左边的筷子</span></span>
<span class="hljs-keyword">private</span> Chopstick left;
<span class="hljs-comment">// <span class="zh-hans">右边的筷子</span></span>
<span class="hljs-keyword">private</span> Chopstick right;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> id;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> ponderFactor;
<span class="hljs-keyword">private</span> Random rand = <span class="hljs-keyword">new</span> Random(<span class="hljs-number">47</span>);
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Philosopher</span><span class="hljs-params">(Chopstick left, Chopstick right, <span class="hljs-keyword">int</span> id, <span class="hljs-keyword">int</span> ponder)</span> </span>{
<span class="hljs-keyword">this</span>.left = left;
<span class="hljs-keyword">this</span>.right = right;
<span class="hljs-keyword">this</span>.id = id;
<span class="hljs-keyword">this</span>.ponderFactor = ponder;
}
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pause</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>{
<span class="hljs-keyword">if</span> (ponderFactor == <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span>;
}
TimeUnit.MICROSECONDS.sleep(rand.nextInt(ponderFactor * <span class="hljs-number">250</span>));
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">while</span> (!Thread.interrupted()) {
System.out.println(<span class="hljs-keyword">this</span> + <span class="hljs-string">" thinking..."</span>);
pause();
System.out.println(<span class="hljs-keyword">this</span> + <span class="hljs-string">" grabbing right"</span>);
right.take();
System.out.println(<span class="hljs-keyword">this</span> + <span class="hljs-string">" grabbing left"</span>);
left.take();
System.out.println(<span class="hljs-keyword">this</span> + <span class="hljs-string">" eating....."</span>);
pause();
right.drop();
left.drop();
}
} <span class="hljs-keyword">catch</span> (Exception e) {
System.out.println(<span class="hljs-keyword">this</span> + <span class="hljs-string">" exiting via interrupt....."</span>);
e.printStackTrace();
}
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> <span class="hljs-string">"Philosopher{"</span> +
<span class="hljs-string">"id="</span> + id +
<span class="hljs-string">'}'</span>;
}
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Chopstick</span> </span>{
<span class="hljs-comment">// <span class="zh-hans">筷子是否被使用</span></span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> taken = <span class="hljs-keyword">false</span>;
<span class="hljs-comment">//<span class="zh-hans">拿筷子</span></span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">take</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>{
<span class="hljs-keyword">while</span> (taken) {
wait();
taken = <span class="hljs-keyword">true</span>;
}
}
<span class="hljs-comment">//<span class="zh-hans">放下筷子</span></span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">drop</span><span class="hljs-params">()</span> </span>{
taken = <span class="hljs-keyword">false</span>;
notifyAll();
}
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DeadLockingDining</span> </span>{
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
Chopstick[] chopsticks = <span class="hljs-keyword">new</span> Chopstick[<span class="hljs-number">5</span>];
ExecutorService exec = Executors.newCachedThreadPool();
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {
chopsticks[i] = <span class="hljs-keyword">new</span> Chopstick();
}
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {
<span class="hljs-comment">// <span class="zh-hans">把思考时间减少至</span>0<span class="zh-hans">,即</span>ponder = 0<span class="zh-hans">,会更容易发生死锁。</span></span>
exec.execute(<span class="hljs-keyword">new</span> Philosopher(chopsticks[i], chopsticks[(i + <span class="hljs-number">1</span>) % <span class="hljs-number">5</span>], i, <span class="hljs-number">5</span>));
}
exec.shutdown();
}
}
</code></pre><h4 data-source-line="1063" id="死锁发生的条件"><a class="markdownIt-Anchor" href="#死锁发生的条件"></a>死锁发生的条件?</h4><p data-source-line="1065">四者缺一不可!</p><ul data-source-line="1067"><li>至少有一个资源不能共享。</li><li>至少有一个任务必须持有一个资源,且正在等待一个被别的任务占用的资源。</li><li>资源不能被抢占。我的理解是永久占有。</li><li>task1等待task2的资源...然后task N等待task1资源,也就是得形成一个环。</li></ul><h4 data-source-line="1072" id="解决死锁问题"><a class="markdownIt-Anchor" href="#解决死锁问题"></a>解决死锁问题</h4><p data-source-line="1074">既然知道了死锁发生的条件,就可以按照这个思路去解决上面的哲学家问题,四者破其一即可。</p><p data-source-line="1076">例如,打破原来哲学家们统一先拿右面的筷子,然后再拿左边的筷子的顺序,可以让最后一个哲学家顺序正好相反,从而打破这个循环等待条件。</p><h2 data-source-line="1078" id="多线程的特性"><a class="markdownIt-Anchor" href="#多线程的特性"></a>多线程的特性:</h2><h3 data-source-line="1080" id="原子性synchronized"><a class="markdownIt-Anchor" href="#原子性synchronized"></a>原子性:synchronized</h3><p data-source-line="1081">是指即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。</p><h3 data-source-line="1083" id="可见性volatile"><a class="markdownIt-Anchor" href="#可见性volatile"></a>可见性:volatile</h3><p data-source-line="1085">是指当一个线程修改了某一个变量的值,其他线程是否是否立即知道这个修改。</p><h3 data-source-line="1087" id="有序性"><a class="markdownIt-Anchor" href="#有序性"></a>有序性:</h3><p data-source-line="1089">我们只是习惯地认为代码的执行是依次执行的,但是在程序执行时可能会发生指令重排等问题。</p></div></article><body></body></html>