-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.xml
992 lines (866 loc) · 165 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[JellyBin's博客]]></title>
<link href="/atom.xml" rel="self"/>
<link href="http://jellybins.github.io//"/>
<updated>2016-06-14T07:29:41.168Z</updated>
<id>http://jellybins.github.io//</id>
<author>
<name><![CDATA[Jelly Bins]]></name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title><![CDATA[路由器OpenWrt定制相关]]></title>
<link href="http://jellybins.github.io/2016/06/14/%E8%B7%AF%E7%94%B1%E5%99%A8OpenWrt%E5%AE%9A%E5%88%B6%E7%9B%B8%E5%85%B3/"/>
<id>http://jellybins.github.io/2016/06/14/路由器OpenWrt定制相关/</id>
<published>2016-06-14T07:21:54.000Z</published>
<updated>2016-06-14T07:29:41.168Z</updated>
<content type="html"><![CDATA[<ul>
<li><p>国内较快速的软件源:</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">src<span class="regexp">/gz chaos_calmer_base http:/</span><span class="regexp">/openwrt.mirrors.ustc.edu.cn/</span>chaos_calmer<span class="regexp">/15.05.1/</span>ar71xx<span class="regexp">/nand/</span>packages/base</span><br><span class="line">src<span class="regexp">/gz chaos_calmer_luci http:/</span><span class="regexp">/openwrt.mirrors.ustc.edu.cn/</span>chaos_calmer<span class="regexp">/15.05.1/</span>ar71xx<span class="regexp">/nand/</span>packages/luci</span><br><span class="line">src<span class="regexp">/gz chaos_calmer_packages http:/</span><span class="regexp">/openwrt.mirrors.ustc.edu.cn/</span>chaos_calmer<span class="regexp">/15.05.1/</span>ar71xx<span class="regexp">/nand/</span>packages/packages</span><br><span class="line">src<span class="regexp">/gz chaos_calmer_routing http:/</span><span class="regexp">/openwrt.mirrors.ustc.edu.cn/</span>chaos_calmer<span class="regexp">/15.05.1/</span>ar71xx<span class="regexp">/nand/</span>packages/routing</span><br><span class="line">src<span class="regexp">/gz chaos_calmer_telephony http:/</span><span class="regexp">/openwrt.mirrors.ustc.edu.cn/</span>chaos_calmer<span class="regexp">/15.05.1/</span>ar71xx<span class="regexp">/nand/</span>packages/telephony</span><br><span class="line">src<span class="regexp">/gz chaos_calmer_management http:/</span><span class="regexp">/openwrt.mirrors.ustc.edu.cn/</span>chaos_calmer<span class="regexp">/15.05.1/</span>ar71xx<span class="regexp">/nand/</span>packages/management</span><br></pre></td></tr></table></figure>
</li>
<li><p>openwrt降级安装ipk</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">opkg <span class="operator"><span class="keyword">install</span> ipkname.ipk <span class="comment">--force-downgrade</span></span></span><br></pre></td></tr></table></figure></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<ul>
<li><p>国内较快速的软件源:</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="l]]>
</summary>
<category term="系统定制" scheme="http://jellybins.github.io/tags/%E7%B3%BB%E7%BB%9F%E5%AE%9A%E5%88%B6/"/>
<category term="路由器" scheme="http://jellybins.github.io/tags/%E8%B7%AF%E7%94%B1%E5%99%A8/"/>
<category term="OpenWrt" scheme="http://jellybins.github.io/categories/OpenWrt/"/>
</entry>
<entry>
<title><![CDATA[spring security命名空间配置]]></title>
<link href="http://jellybins.github.io/2016/06/14/spring-security%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E9%85%8D%E7%BD%AE/"/>
<id>http://jellybins.github.io/2016/06/14/spring-security命名空间配置/</id>
<published>2016-06-14T06:43:52.000Z</published>
<updated>2016-06-14T06:47:55.747Z</updated>
<content type="html"><![CDATA[<hr>
<p>applicationContext-security.xml命名空间配置,官方提供了两种配置方案</p>
<ul>
<li>第一种、命名空间用beans开头,但是在配置中一直需要用<security:*>来配置。</security:*></li>
</ul>
<figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><beans <span class="variable">xmlns=</span><span class="string">"http://www.springframework.org/schema/beans"</span></span><br><span class="line"> xmlns:<span class="variable">security=</span><span class="string">"http://www.springframework.org/schema/security"</span></span><br><span class="line"> xmlns:<span class="variable">xsi=</span><span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span><br><span class="line"> xsi:<span class="variable">schemaLocation=</span><span class="string">"http://www.springframework.org/schema/beans</span><br><span class="line"> http://www.springframework.org/schema/beans/spring-beans-3.0.xsd</span><br><span class="line"> http://www.springframework.org/schema/security</span><br><span class="line"> http://www.springframework.org/schema/security/spring-security.xsd"</span>></span><br><span class="line"> ...</span><br><span class="line"></beans></span><br></pre></td></tr></table></figure>
<a id="more"></a>
<ul>
<li>第二种、命名空间用security开头,在配置中不需要security前缀,但是bean的配置需要用<beans:bean>配置。</beans:bean></li>
</ul>
<figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><beans:beans <span class="variable">xmlns=</span><span class="string">"http://www.springframework.org/schema/security"</span></span><br><span class="line"> xmlns:<span class="variable">beans=</span><span class="string">"http://www.springframework.org/schema/beans"</span></span><br><span class="line"> xmlns:<span class="variable">xsi=</span><span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span><br><span class="line"> xsi:<span class="variable">schemaLocation=</span><span class="string">"http://www.springframework.org/schema/beans</span><br><span class="line"> http://www.springframework.org/schema/beans/spring-beans-3.0.xsd</span><br><span class="line"> http://www.springframework.org/schema/security</span><br><span class="line"> http://www.springframework.org/schema/security/spring-security.xsd"</span>></span><br><span class="line"> ...</span><br><span class="line"></beans:beans></span><br></pre></td></tr></table></figure>
<blockquote>
<p>第一行的xmlns=”<a href="http://www.springframework.org/schema/security"决定此配置文件默认schema为security,故涉及security命名空间元素都可省略前缀security。同理,当xmlns="http://www.springframework.org/schema/beans"时,则配置文件默认schema为beans,故可省略beans前缀,直接使用" target="_blank" rel="external">http://www.springframework.org/schema/security"决定此配置文件默认schema为security,故涉及security命名空间元素都可省略前缀security。同理,当xmlns="http://www.springframework.org/schema/beans"时,则配置文件默认schema为beans,故可省略beans前缀,直接使用</a><bean>定义beans命名空间元素。</bean></p>
</blockquote>
<p>PS:schemaLocation里的URI可在对应的类库包的META-INF的*.schemas文件中查看。</p>
<p><a href="http://docs.spring.io/spring-security/site/docs/3.2.0.RELEASE/reference/htmlsingle/#ns-config" target="_blank" rel="external">官方链接</a></p>
]]></content>
<summary type="html">
<![CDATA[<hr>
<p>applicationContext-security.xml命名空间配置,官方提供了两种配置方案</p>
<ul>
<li>第一种、命名空间用beans开头,但是在配置中一直需要用<security:*>来配置。</li>
</ul>
<figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><beans <span class="variable">xmlns=</span><span class="string">"http://www.springframework.org/schema/beans"</span></span><br><span class="line"> xmlns:<span class="variable">security=</span><span class="string">"http://www.springframework.org/schema/security"</span></span><br><span class="line"> xmlns:<span class="variable">xsi=</span><span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span><br><span class="line"> xsi:<span class="variable">schemaLocation=</span><span class="string">"http://www.springframework.org/schema/beans</span><br><span class="line"> http://www.springframework.org/schema/beans/spring-beans-3.0.xsd</span><br><span class="line"> http://www.springframework.org/schema/security</span><br><span class="line"> http://www.springframework.org/schema/security/spring-security.xsd"</span>></span><br><span class="line"> ...</span><br><span class="line"></beans></span><br></pre></td></tr></table></figure>]]>
</summary>
<category term="Spring Security 3" scheme="http://jellybins.github.io/tags/Spring-Security-3/"/>
<category term="命名空间" scheme="http://jellybins.github.io/tags/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4/"/>
<category term="权限框架" scheme="http://jellybins.github.io/tags/%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6/"/>
<category term="Spring" scheme="http://jellybins.github.io/categories/Spring/"/>
</entry>
<entry>
<title><![CDATA[html中button元素type属性默认值问题]]></title>
<link href="http://jellybins.github.io/2016/05/26/html%E4%B8%ADbutton%E5%85%83%E7%B4%A0type%E5%B1%9E%E6%80%A7%E9%BB%98%E8%AE%A4%E5%80%BC%E9%97%AE%E9%A2%98/"/>
<id>http://jellybins.github.io/2016/05/26/html中button元素type属性默认值问题/</id>
<published>2016-05-26T03:33:16.000Z</published>
<updated>2016-05-31T08:58:28.194Z</updated>
<content type="html"><![CDATA[<p>根据前端设计原则html->内容,css->样式,js->动作一般会将按钮点击事件在js中绑定,但是button元素type属性默认值在不同浏览器有差异,故在除IE以外浏览器中会造成多次提交或提交表单后页面又刷新的问题,故记录button元素用法如下:</p>
<a id="more"></a>
<p>HTML DOM Button 对象</p>
<p>定义和用法:type属性设置或返回按钮的类型。<br><strong>提示:请始终为按钮规定 type 属性。IE 浏览器的默认类型是 “button”,而在其他浏览器中(以及在 W3C 规范中)是 “submit”。</strong></p>
<p>语法:buttonObject.type=value,可选值及描述如下表:<br>| 值 | 描述 |<br>| :——: | :——: |<br>| submit |button 是提交按钮(是 IE 之外的所有浏览器的默认值)|<br>| button |button 是可点击的按钮(IE 的默认值)|<br>| reset |button 是重置按钮(清除表单数据)|</p>
]]></content>
<summary type="html">
<![CDATA[<p>根据前端设计原则html->内容,css->样式,js->动作一般会将按钮点击事件在js中绑定,但是button元素type属性默认值在不同浏览器有差异,故在除IE以外浏览器中会造成多次提交或提交表单后页面又刷新的问题,故记录button元素用法如下:</p>]]>
</summary>
<category term="属性默认值" scheme="http://jellybins.github.io/tags/%E5%B1%9E%E6%80%A7%E9%BB%98%E8%AE%A4%E5%80%BC/"/>
<category term="浏览器差异" scheme="http://jellybins.github.io/tags/%E6%B5%8F%E8%A7%88%E5%99%A8%E5%B7%AE%E5%BC%82/"/>
<category term="Html" scheme="http://jellybins.github.io/categories/Html/"/>
</entry>
<entry>
<title><![CDATA[Spring Security3使用SecurityContextHolder取不到用户]]></title>
<link href="http://jellybins.github.io/2016/04/23/Spring-Security3%E4%BD%BF%E7%94%A8SecurityContextHolder%E5%8F%96%E4%B8%8D%E5%88%B0%E7%94%A8%E6%88%B7/"/>
<id>http://jellybins.github.io/2016/04/23/Spring-Security3使用SecurityContextHolder取不到用户/</id>
<published>2016-04-23T10:35:15.000Z</published>
<updated>2016-06-14T06:47:39.131Z</updated>
<content type="html"><![CDATA[<h2 id="在Java中取用户信息的代码:">在Java中取用户信息的代码:</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Object principal = SecurityContextHolder.getContext()</span><br><span class="line"> .getAuthentication().getPrincipal();</span><br><span class="line"><span class="keyword">if</span> (principal <span class="keyword">instanceof</span> UserDetails) {</span><br><span class="line"> username = ((UserDetails) principal).getUsername();</span><br><span class="line"> Iterator it = ((UserDetails) principal).getAuthorities().iterator();</span><br><span class="line"> String authority = <span class="string">""</span>;</span><br><span class="line"> <span class="keyword">while</span> (it.hasNext()) {</span><br><span class="line"> authority = ((GrantedAuthority) it.next()).getAuthority();</span><br><span class="line"> System.out.println(<span class="string">"Authority:"</span> + authority);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="spring_security_3的配置示例:applicationContext-security-xml">spring security 3的配置示例:applicationContext-security.xml</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="title">intercept-url</span> <span class="attribute">pattern</span>=<span class="value">"/dwr/**"</span> <span class="attribute">filters</span>=<span class="value">"none"</span>/></span> </span><br><span class="line"><span class="tag"><<span class="title">intercept-url</span> <span class="attribute">pattern</span>=<span class="value">"/css/**"</span> <span class="attribute">filters</span>=<span class="value">"none"</span>/></span> </span><br><span class="line"><span class="tag"><<span class="title">intercept-url</span> <span class="attribute">pattern</span>=<span class="value">"/images/**"</span> <span class="attribute">filters</span>=<span class="value">"none"</span>/></span> </span><br><span class="line"><span class="tag"><<span class="title">intercept-url</span> <span class="attribute">pattern</span>=<span class="value">"/scripts/**"</span> <span class="attribute">filters</span>=<span class="value">"none"</span>/></span> </span><br><span class="line"><span class="tag"><<span class="title">intercept-url</span> <span class="attribute">pattern</span>=<span class="value">"/index.html*"</span> <span class="attribute">filters</span>=<span class="value">"none"</span>/></span> </span><br><span class="line"><span class="tag"><<span class="title">intercept-url</span> <span class="attribute">pattern</span>=<span class="value">"/mydefault/**"</span> <span class="attribute">filters</span>=<span class="value">"none"</span>/></span> </span><br><span class="line"><span class="comment"><!-- </span><br><span class="line"><intercept-url pattern="/admin/**" filters="none"/> </span><br><span class="line">--></span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>当你的servlet、action或Spring MVC的C(Control)相对应的url是以/mydefalut/开头,如果你要使用SecurityContextHolder获取用户信息,那么我告诉你,你想都别想。绝对是不可能的!</p>
<p>当你的servlet、action或Spring MVC的C相对应的url是以/admin/开头(或者其它没有没有配置filter为none的url),如果你要使用SecurityContextHolder获取用户信息,那么我想你是没有问题的。</p>
</blockquote>
<h2 id="产生原因:">产生原因:</h2><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><<span class="attribute">filter</span>> </span><br><span class="line"> <<span class="attribute">filter</span>-name>springSecurityFilterChain</<span class="attribute">filter</span>-name> </span><br><span class="line"> <<span class="attribute">filter</span>-class>org<span class="class">.springframework</span><span class="class">.web</span><span class="class">.filter</span><span class="class">.DelegatingFilterProxy</span></<span class="attribute">filter</span>-class> </span><br><span class="line"></<span class="attribute">filter</span>> </span><br><span class="line"> </span><br><span class="line"><<span class="attribute">filter</span>-mapping> </span><br><span class="line"> <<span class="attribute">filter</span>-name>springSecurityFilterChain</<span class="attribute">filter</span>-name> </span><br><span class="line"> <url-pattern><span class="comment">/*</url-pattern> </span><br><span class="line"></filter-mapping></span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>Spring Security的工作原理:Spring Security就是一个filter,如果你把Spring Security的配置文件中某个url-pattern 的filter配置为none,那么与这个url相关的请求,Spring Security是不会干活滴!</p>
</blockquote>
<ul>
<li>PS:<a href="http://blog.csdn.net/generalyy0/article/details/8467675" target="_blank" rel="external">原文出处</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="在Java中取用户信息的代码:">在Java中取用户信息的代码:</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Object principal = SecurityContextHolder.getContext()</span><br><span class="line"> .getAuthentication().getPrincipal();</span><br><span class="line"><span class="keyword">if</span> (principal <span class="keyword">instanceof</span> UserDetails) {</span><br><span class="line"> username = ((UserDetails) principal).getUsername();</span><br><span class="line"> Iterator it = ((UserDetails) principal).getAuthorities().iterator();</span><br><span class="line"> String authority = <span class="string">""</span>;</span><br><span class="line"> <span class="keyword">while</span> (it.hasNext()) {</span><br><span class="line"> authority = ((GrantedAuthority) it.next()).getAuthority();</span><br><span class="line"> System.out.println(<span class="string">"Authority:"</span> + authority);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]>
</summary>
<category term="Spring Security 3" scheme="http://jellybins.github.io/tags/Spring-Security-3/"/>
<category term="权限框架" scheme="http://jellybins.github.io/tags/%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6/"/>
<category term="Spring" scheme="http://jellybins.github.io/categories/Spring/"/>
</entry>
<entry>
<title><![CDATA[easyui表格前台删除及jquery中attr和prop区别]]></title>
<link href="http://jellybins.github.io/2016/03/24/easyui%E8%A1%A8%E6%A0%BC%E5%89%8D%E5%8F%B0%E5%88%A0%E9%99%A4%E5%8F%8Ajquery%E4%B8%ADattr%E5%92%8Cprop%E5%8C%BA%E5%88%AB/"/>
<id>http://jellybins.github.io/2016/03/24/easyui表格前台删除及jquery中attr和prop区别/</id>
<published>2016-03-24T03:32:52.000Z</published>
<updated>2016-05-31T08:56:36.643Z</updated>
<content type="html"><![CDATA[<h2 id="easyui_deleteRow方法批量执行出错">easyui deleteRow方法批量执行出错</h2><p>easyui的datatgrid表格组件deleteRow(rowIndex)删除行方法中,删除一行后会立即调用opts.view.deleteRow.call(opts.view,_4d2,_4d3),刷新页面上剩余行的rowIndex,rowIndex发生改变后,原来rows的数据也会发生改变,删除会出错,故把这个rows复制给另外一个数组,每次删除再重新查找rowIndex。</p>
<a id="more"></a>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">del</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> rows = $(<span class="string">'#tt'</span>).datagrid(<span class="string">"getSelections"</span>); </span><br><span class="line"> <span class="keyword">var</span> copyRows = [];</span><br><span class="line"> <span class="keyword">for</span> ( <span class="keyword">var</span> j= <span class="number">0</span>; j < rows.length; j++) {</span><br><span class="line"> copyRows.push(rows[j]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i =<span class="number">0</span>;i<copyRows.length;i++){ </span><br><span class="line"> <span class="keyword">var</span> index = $(<span class="string">'#tt'</span>).datagrid(<span class="string">'getRowIndex'</span>,copyRows[i]);</span><br><span class="line"> $(<span class="string">'#tt'</span>).datagrid(<span class="string">'deleteRow'</span>,index); </span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="jquery中attr和prop的区别">jquery中attr和prop的区别</h2><ul>
<li>对于HTML元素本身就带有的固有属性,在处理时,使用prop方法。</li>
<li>对于HTML元素我们自己自定义的DOM属性,在处理时,使用attr方法。</li>
</ul>
<p>例1: </p>
<p><code><a href="http://www.baidu.com" target="_self" class="btn">百度</a></code><br>这个例子里<a>元素的DOM属性有“href、target和class”,这些属性就是<a>元素本身就带有的属性,也是W3C标准里就包含有这几个属性,或者说在IDE里能够智能提示出的属性,这些就叫做固有属性。处理这些属性时,建议使用prop方法。</a></a></p>
<p><code><a href="#" id="link1" action="delete">删除</a></code><br>这个例子里<a>元素的DOM属性有“href、id和action”,很明显,前两个是固有属性,而后面一个“action”属性是我们自己自定义上去的,<a>元素本身是没有这个属性的。这种就是自定义的DOM属性。处理这些属性时,建议使用attr方法。使用prop方法取值和设置属性值时,都会返回undefined值。</a></a></p>
<p>例2:</p>
<p><code><input id="chk1" type="checkbox" />是否可见</code><br><code><input id="chk2" type="checkbox" checked="checked" />是否可见</code><br>像checkbox,radio和select这样的元素,选中属性对应“checked”和“selected”,这些也属于固有属性,因此需要使用prop方法去操作才能获得正确的结果。<br>使用prop结果如下:<br><figure class="highlight openscad"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$<span class="params">(<span class="string">"#chk1"</span>)</span>.prop<span class="params">(<span class="string">"checked"</span>)</span> == <span class="literal">false</span></span><br><span class="line">$<span class="params">(<span class="string">"#chk2"</span>)</span>.prop<span class="params">(<span class="string">"checked"</span>)</span> == <span class="literal">true</span></span><br></pre></td></tr></table></figure></p>
<p>如果上面使用attr方法,则会出现:<br><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"#chk1"</span>).<span class="function"><span class="title">attr</span><span class="params">(<span class="string">"checked"</span>)</span></span> == undefined</span><br><span class="line">$(<span class="string">"#chk2"</span>).<span class="function"><span class="title">attr</span><span class="params">(<span class="string">"checked"</span>)</span></span> == <span class="string">"checked"</span></span><br></pre></td></tr></table></figure></p>
<p>另注意:checkbox、radio、select元素中只要含有checked属性或selected属性即为勾选或选中状态。即使<code>checked = "false"</code>或<code>selected="false"</code>。</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="easyui_deleteRow方法批量执行出错">easyui deleteRow方法批量执行出错</h2><p>easyui的datatgrid表格组件deleteRow(rowIndex)删除行方法中,删除一行后会立即调用opts.view.deleteRow.call(opts.view,_4d2,_4d3),刷新页面上剩余行的rowIndex,rowIndex发生改变后,原来rows的数据也会发生改变,删除会出错,故把这个rows复制给另外一个数组,每次删除再重新查找rowIndex。</p>]]>
</summary>
<category term="easyui" scheme="http://jellybins.github.io/tags/easyui/"/>
<category term="jquery" scheme="http://jellybins.github.io/tags/jquery/"/>
<category term="JavaScript" scheme="http://jellybins.github.io/categories/JavaScript/"/>
</entry>
<entry>
<title><![CDATA[OpenWrt使用crontab执行定时任务说明]]></title>
<link href="http://jellybins.github.io/2016/03/21/OpenWrt%E4%BD%BF%E7%94%A8crontab%E6%89%A7%E8%A1%8C%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E8%AF%B4%E6%98%8E/"/>
<id>http://jellybins.github.io/2016/03/21/OpenWrt使用crontab执行定时任务说明/</id>
<published>2016-03-21T03:32:16.000Z</published>
<updated>2016-05-31T04:43:51.457Z</updated>
<content type="html"><![CDATA[<p>列出当前的默认计划任务列表:<code>crontab -l</code><br>删除当前的默认计划任务列表:<code>crontab -r</code><br>修改当前的默认计划任务列表:<code>crontab -e</code></p>
<p>计划任务列表的格式:</p>
<p><code>[minute] [hour] [day of month] [month] [day of week] [program to be run]</code></p>
<a id="more"></a>
<p>其中各个参数的取值范围是:<br><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">minute</span><span class="params">(<span class="number">0</span>-<span class="number">59</span>)</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">hour</span><span class="params">(<span class="number">0</span>-<span class="number">23</span>)</span></span></span><br><span class="line"></span><br><span class="line">day of <span class="function"><span class="title">month</span><span class="params">(<span class="number">1</span>-<span class="number">31</span>)</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">month</span><span class="params">(<span class="number">1</span>-<span class="number">12</span>)</span></span></span><br><span class="line"></span><br><span class="line">day of <span class="function"><span class="title">week</span><span class="params">(<span class="number">0</span>-<span class="number">7</span>,<span class="number">0</span> or <span class="number">7</span> is Sun)</span></span></span><br></pre></td></tr></table></figure></p>
<p>每个参数里的取值可以有4种间隔符:<br><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">* </span>表示任意</span><br><span class="line"></span><br><span class="line">– 表示范围</span><br><span class="line"></span><br><span class="line">, 表示枚举多个值</span><br><span class="line"></span><br><span class="line">/ 表示每隔</span><br></pre></td></tr></table></figure></p>
<p>例如:</p>
<ul>
<li><p>周一到周五每天晚上23:30执行client -k:<code>30 23 * * 1-5 /bin/client -k</code></p>
</li>
<li><p>每天每隔10分钟执行date:<code>*/10 * * * * date</code></p>
</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>列出当前的默认计划任务列表:<code>crontab -l</code><br>删除当前的默认计划任务列表:<code>crontab -r</code><br>修改当前的默认计划任务列表:<code>crontab -e</code></p>
<p>计划任务列表的格式:</p>
<p><code>[minute] [hour] [day of month] [month] [day of week] [program to be run]</code></p>]]>
</summary>
<category term="OpenWrt" scheme="http://jellybins.github.io/tags/OpenWrt/"/>
<category term="定时任务" scheme="http://jellybins.github.io/tags/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/"/>
<category term="Linux" scheme="http://jellybins.github.io/categories/Linux/"/>
</entry>
<entry>
<title><![CDATA[以MIME为application-json方式提交参数示例]]></title>
<link href="http://jellybins.github.io/2016/02/13/%E4%BB%A5MIME%E4%B8%BAapplication-json%E6%96%B9%E5%BC%8F%E6%8F%90%E4%BA%A4%E5%8F%82%E6%95%B0%E7%A4%BA%E4%BE%8B/"/>
<id>http://jellybins.github.io/2016/02/13/以MIME为application-json方式提交参数示例/</id>
<published>2016-02-13T07:36:20.000Z</published>
<updated>2016-05-31T06:31:49.962Z</updated>
<content type="html"><![CDATA[<p>以 application/json作为请求头格式可以提交json字符串作为request参数。。服务端直接读取request的输入流即可拿到该完整字符串再使用jsonlib库转化为JSON对象,示例代码如下:</p>
<a id="more"></a>
<ul>
<li><p>浏览器端:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj= {</span><br><span class="line"> method:<span class="string">"login"</span>,</span><br><span class="line"> staff_id:<span class="string">"staff"</span>,</span><br><span class="line"> password:<span class="string">"123"</span></span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line">$.ajax({</span><br><span class="line"> url: <span class="string">"index.do"</span>,</span><br><span class="line"> type:<span class="string">"post"</span>,</span><br><span class="line"> contentType:<span class="string">"application/json"</span>,</span><br><span class="line"> data:<span class="built_in">JSON</span>.stringify(obj),</span><br><span class="line"> success: <span class="function"><span class="keyword">function</span>(<span class="params"> data </span>) </span>{</span><br><span class="line"> alert(<span class="string">"aaa"</span>);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
</li>
<li><p>服务端:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">BufferedReader br = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(</span><br><span class="line"> req.getInputStream(), <span class="string">"UTF-8"</span>));</span><br><span class="line">String temp;</span><br><span class="line"><span class="keyword">while</span> ((temp = br.readLine()) != <span class="keyword">null</span>) {</span><br><span class="line"> sb.append(temp);</span><br><span class="line">}</span><br><span class="line">br.close();</span><br><span class="line">result = <span class="keyword">new</span> String(sb.toString());</span><br><span class="line">result = URLDecoder.decode(result, <span class="string">"UTF-8"</span>);</span><br><span class="line"></span><br><span class="line">JSONObject jsonObject = <span class="keyword">new</span> JSONObject();</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> jsonObject = JSONObject.fromObject(result);</span><br><span class="line">} <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> String errorInfo = <span class="string">"{\"loginFlag\":\"9999\",\"loginInfo\":\"入参格式有误\"}"</span>;</span><br><span class="line"> out.print(errorInfo);</span><br><span class="line"> out.flush();</span><br><span class="line"> out.close();</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">String method = jsonObject.getString(<span class="string">"method"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (method!=<span class="keyword">null</span>&&!<span class="string">""</span>.equals(method)){</span><br><span class="line"> <span class="keyword">if</span>(method.equals(<span class="string">"login"</span>)) {</span><br><span class="line"> <span class="keyword">this</span>.login(jsonObject, resp);</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(method.equals(<span class="string">"anotherm"</span>)){</span><br><span class="line"> <span class="keyword">this</span>.anotherm(jsonObject,resp);</span><br><span class="line"> }</span><br><span class="line">} <span class="keyword">else</span>{</span><br><span class="line"> String errorInfo = <span class="string">"{\"loginFlag\":\"9999\",\"loginInfo\":\"入参格式有误\"}"</span>;</span><br><span class="line"> out.print(errorInfo);</span><br><span class="line"> out.flush();</span><br><span class="line"> out.close();</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>以 application/json作为请求头格式可以提交json字符串作为request参数。。服务端直接读取request的输入流即可拿到该完整字符串再使用jsonlib库转化为JSON对象,示例代码如下:</p>]]>
</summary>
<category term="JSON" scheme="http://jellybins.github.io/tags/JSON/"/>
<category term="MIME" scheme="http://jellybins.github.io/tags/MIME/"/>
<category term="参数提交" scheme="http://jellybins.github.io/tags/%E5%8F%82%E6%95%B0%E6%8F%90%E4%BA%A4/"/>
<category term="JavaScript" scheme="http://jellybins.github.io/categories/JavaScript/"/>
</entry>
<entry>
<title><![CDATA[线程sleep不释放锁代码验证说明]]></title>
<link href="http://jellybins.github.io/2016/02/11/%E7%BA%BF%E7%A8%8Bsleep%E4%B8%8D%E4%BC%9A%E9%87%8A%E6%94%BE%E9%94%81%E4%BB%A3%E7%A0%81%E9%AA%8C%E8%AF%81%E8%AF%B4%E6%98%8E/"/>
<id>http://jellybins.github.io/2016/02/11/线程sleep不会释放锁代码验证说明/</id>
<published>2016-02-11T03:33:48.000Z</published>
<updated>2016-05-31T08:53:28.195Z</updated>
<content type="html"><![CDATA[<h2 id="关于sleep不释放锁的说明">关于sleep不释放锁的说明</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> run(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="keyword">true</span>){</span><br><span class="line"> <span class="keyword">synchronized</span>(<span class="keyword">this</span>){</span><br><span class="line"> <span class="comment">//<span class="doctag"><span class="keyword">todo</span></span> sth</span></span><br><span class="line"> Thread.sleep()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述代码执行会出现多个线程都能执行同步代码块情况,原因并非是sleep释放锁,而是每一次同步代码块执行完毕(循环一次完毕)线程会自动释放锁。流程图如下:</p>
</blockquote>
<a id="more"></a>
<p><img src="/images/sleep1.jpg" alt="sleep1"></p>
<blockquote>
<p>一次循环开始—请求并获得锁—todo sth-当前线程休眠—同步代码块执行一次完毕,锁自动释放(一次循环结束,其它线程获得锁执行)-下一次循环</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> run(){</span><br><span class="line"> <span class="keyword">synchronized</span>(<span class="keyword">this</span>){</span><br><span class="line"> <span class="keyword">while</span>(<span class="keyword">true</span>){</span><br><span class="line"> <span class="comment">//<span class="doctag"><span class="keyword">todo</span></span> sth</span></span><br><span class="line"> Thread.sleep()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述代码才能表明sleep并不会释放对象锁。流程图如下:</p>
</blockquote>
<p><img src="/images/sleep2.jpg" alt="sleep2"></p>
<blockquote>
<p>请求获得锁—循环开始—todo sth—当前线程休眠(sleep也不能使线程释放锁,故别的线程无法执行)—一次循环结束,同步代码块未执行完毕—下一次循环</p>
</blockquote>
<p><strong>关键点:同步代码块执行完毕会自动释放其获得的锁。</strong></p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="关于sleep不释放锁的说明">关于sleep不释放锁的说明</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> run(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="keyword">true</span>){</span><br><span class="line"> <span class="keyword">synchronized</span>(<span class="keyword">this</span>){</span><br><span class="line"> <span class="comment">//<span class="doctag"><span class="keyword">todo</span></span> sth</span></span><br><span class="line"> Thread.sleep()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述代码执行会出现多个线程都能执行同步代码块情况,原因并非是sleep释放锁,而是每一次同步代码块执行完毕(循环一次完毕)线程会自动释放锁。流程图如下:</p>
</blockquote>]]>
</summary>
<category term="线程" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B/"/>
<category term="线程休眠" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B%E4%BC%91%E7%9C%A0/"/>
<category term="线程锁" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B%E9%94%81/"/>
<category term="Java" scheme="http://jellybins.github.io/categories/Java/"/>
</entry>
<entry>
<title><![CDATA[Android定时任务详解]]></title>
<link href="http://jellybins.github.io/2016/01/26/Android%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E8%AF%A6%E8%A7%A3/"/>
<id>http://jellybins.github.io/2016/01/26/Android定时任务详解/</id>
<published>2016-01-26T09:06:43.000Z</published>
<updated>2016-01-27T00:01:49.027Z</updated>
<content type="html"><![CDATA[<p>Android系统下实现指定时长后执行某任务或周期性执行某项任务(以下统称为定时任务)通常有以下两种方式:1,定时任务实现TimerTask,Timer控制定时任务的启动,取消。2,通过PendingIntent执行定时任务,AlarmManager(安卓的闹钟服务)来控制定时任务的启动和取消。两者同样能实现,不同之处在于前者是Java范畴的定时任务,而Android官方推荐的定时任务实现则是后者。但是不一定完全遵照上述标准,特殊情况下还要特殊处理,比如这篇文章最后提到的问题。</p>
<a id="more"></a>
<h2 id="TimerTask实现方式">TimerTask实现方式</h2><ol>
<li><p>定时任务(以AlarmTask为例)实现TimerTask。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AlarmTask</span> <span class="keyword">extends</span> <span class="title">TimerTask</span> </span>{</span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">//任务代码写在此处</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>Timer类启动和取消AlarmTask。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Timer timer = <span class="keyword">new</span> Timer();</span><br><span class="line"></span><br><span class="line">TimerTask alarmTask = <span class="keyword">new</span> AlarmTask();</span><br><span class="line"></span><br><span class="line">timer.schedule(alarmTask,<span class="number">0</span>,<span class="number">2</span>*<span class="number">60</span>*<span class="number">1000</span>);<span class="comment">//0毫秒后每2分钟执行该任务一次</span></span><br><span class="line">timer.schedule(alarmTask,<span class="number">1</span>*<span class="number">60</span>*<span class="number">1000</span>);<span class="comment">//1分钟后执行该任务一次</span></span><br><span class="line">·····</span><br><span class="line">alarmTask.cancel();<span class="comment">//取消定时任务</span></span><br></pre></td></tr></table></figure>
</li>
</ol>
<h2 id="AlarmManager实现方式">AlarmManager实现方式</h2><ol>
<li><p>获取系统服务AlarmManager。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);</span><br></pre></td></tr></table></figure>
</li>
<li><p>封装执行定时任务的PendingIntent。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Intent intent = Intent(context,NetworkService.class);</span><br><span class="line">intent.setAction(Constants.ALARM_ACTION);<span class="comment">//自定义的执行定义任务的Action</span></span><br><span class="line"></span><br><span class="line">PendingIntent pendingIntent = PendingIntent.getService(context,requestCode,intent,PendingIntent.FLAG_CANCEL_CURRENT);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>此处仅以getService()方式为例,还可通过getBroadCast()发送广播或getActivity()启动Activity来执行某项固定任务。其中各方法的最后一个参数含有以下常量分别代表不同含义的任务执行效果:</p>
<p>FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。</p>
<p>FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null。</p>
<p>FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。</p>
<p>FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras。</p>
</blockquote>
</li>
<li><p>启动或取消定时任务。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">alarmManager.(AlarmManager.RTC_WAKEUP,triggerAtMills, sender)<span class="comment">//在时间点triggerAtMills执行改任务,如果该时间已过则立即执行</span></span><br><span class="line"></span><br><span class="line">alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,triggerAtMills,intervalAtMills,pendingintent);<span class="comment">//在时间点triggerAtMills执行,如果该时间已过则立即执行,然后以intervalAtMills间隔重复执行该任务</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述方法第一个参数有以下常量分别代表不同含义的定时:</p>
<p>AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;</p>
<p>AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;</p>
<p>AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;</p>
<p>AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;</p>
<p>AlarmManager.POWER_OFF_WAKEUP表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;</p>
</blockquote>
</li>
</ol>
<h2 id="特殊情况特殊处理">特殊情况特殊处理</h2><h3 id="问题来源。">问题来源。</h3><p>开发的一个App中有这样一个需求,通过每隔一段向服务器发送Http请求来更新当前用户在线时间,从而根据系统时间和该用户最后在线时间的差值来大体的判断用户在线状态,原本这个App是针对一个特定型号的手机开发,当时通过TimerTask的方式,设定定时执行此请求,实际使用中也基本满足了需求。后来客户因为原来的手机性能问题全部更换了新的手机,然后使用中发现App切入后台(App设定前台情况下不熄屏),只要手机屏幕熄灭后,用户就会一直处于离线状态。</p>
<h3 id="问题复现。">问题复现。</h3><p>从客户处拿到新换的手机后开始调试,一开始是连着USB线调试查看定时任务的执行情况,然后发现不像客户描述的那样,App后台手机熄屏后,定时任务的Log还是按固有的频率打印的,而且查看用户在线信息的Web页也显示用户一直在线,不像客户讲的那样啊,又开始怀疑是不是用户手机装了数字全家桶或是其它所谓的优化App把我的这个App搞得不正常了,然后又想了下用户实际的使用情况,应该是在断电,3g网络下使用,然后把调试用的USB线拔掉,手机不支持网络调试也无法观察日志打印信息,只能通过上述的Web页面观察用户在线情况,果不其然,问题终于得到复现了,手机熄屏后一段时间,用户一直没有再显示在线过,同时发现之后如果又把USB线连接上,Log信息又开始按设定间隔打印了,用户也显示在线了。</p>
<h3 id="曲折的解决过程。">曲折的解决过程。</h3><p>问题复现后,开始各种搜索,然后发现问题的根源应该就是在非充电情况下,手机熄屏后,CPU会进入休眠状态,原来的TimerTask定时任务将不在执行,而在充电状态下CPU则不会休眠,同时也查找到上述AlarmManager方式实现的定时任务可以通过AlarmManager.RTC_WAKEUP标志来让App在指定的时间唤醒CPU执行定时任务,于是快刀斩乱麻修改为AlarmManager方式,本以为这个问题就这样轻轻松松的解决了,结果却是革命还未成功。</p>
<p>更换实现方式后,再次在手机非充电状态下观察定时任务执行情况,结果发现在设定时间间隔到时时任务还是不执行,表现为用户又离线了,但是好在又耐心等待了一段时间,发现用户又突然在线了一会儿,而且稀奇的是这种情况在充电状态下也一样,查看调试日志是15分钟执行一次(设置2分钟一次,尝试缩短结果还是一样),然后再次查找搜索,最终发现了之前早有耳闻,却还从未遇到过的对齐唤醒问题。国内厂商为了手机省电,在自行定制的ROM中加入了对齐唤醒的技术(貌似小米的MIUI最早的吧),即当有多个App通过AlarmManager方式要唤醒手机CPU执行某项任务时,系统会自动将该唤醒时间延迟到同一个时间点唤醒CPU,查询有哪些应用有唤醒需求,然后在该时间点执行定时任务。这也就造成了在有对齐唤醒机制的手机上,除系统级应用外,其它应用的定时任务的执行时间无法得到保证,这点在MIUI论坛里令好多第三方闹钟开发者怨声载道,这也造成了小米手机上第三方闹钟不准时的问题。就在我痛骂国内手机厂商乱改ROM,感觉问题无解的时候,在官方文档查询到了如下信息:</p>
<blockquote>
<p>With the new batching policy, delivery ordering guarantees are not as strong as they were previously. If the application sets multiple alarms, it is possible that these alarms’ actual delivery ordering may not match the order of their requested delivery times. If your application has strong ordering requirements there are other APIs that you can use to get the necessary behavior; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent).</p>
</blockquote>
<p>上述这段的大意就是在高版本(Level19及以上)的Android系统上已经有了新的方法setWindow()和setExact()来实现精确唤醒CPU,突然感觉胜利就在前方,遂更改调用新的方法,然而定时任务还是不能按时执行,但是跟最初的执行间隔时间比缩短了,结果5分钟一次(同样设定2分钟一次)。再看文档发现了我遗漏了下面的信息,正好解释了为什么用新方法还是不行。</p>
<blockquote>
<p>Note: Beginning in API 19, the trigger time passed to this method is treated as inexact: the alarm will not be delivered before this time, but may be deferred and delivered some time later. The OS will use this policy in order to “batch” alarms together across the entire system, minimizing the number of times the device needs to “wake up” and minimizing battery use. In general, alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.</p>
</blockquote>
<p>上述这段的大意就是从Level19开始,原生系统也采用了对齐唤醒的机制来省电(哇,难道这是Google官方吸收了国内厂商的技术),即使使用新的方法也无法保证能精确执行定时任务,而且同样无论充电还是非充电状态都会延迟,看来这条路是真的行不通了。</p>
<p>就在我再次以为问题真的无解的时候,又发现可以通过PowerManager来控制CPU的休眠状态(屏幕该熄灭还是熄灭),再考虑到AlarmManager即使在充电状态下都无法保证执行时间,而TimerTask却可以,于是尝试采用控制CPU不休眠,定时任务还是TimerTask的方式,虽然控制CPU不休眠会耗电些,但是真正的解决了这个问题。而且可以通过在应用退出后,解除CPU不休眠状态来缩减部分耗电影响,最后附上PowerManager控制CPU不休眠方法。</p>
<h3 id="PowerManager控制手机CPU不休眠。">PowerManager控制手机CPU不休眠。</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">PowerManager powerManager =(PowerManager)getSystemService(Context.POWER_SERVICE);</span><br><span class="line">PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,TAG);<span class="comment">//设置休眠状态</span></span><br><span class="line">wakeLock.acquire();<span class="comment">//锁定休眠状态</span></span><br><span class="line">······</span><br><span class="line">wakeLock.release();<span class="comment">//释放休眠状态锁定,应用退出时调用</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述PowerManager的newWakeLock方法第一个参数有以下常量值代表不同休眠状态,以PARTIAL_WAKE_LOCK为例,该状态锁acquire()之后,CPU会保持非休眠状态,屏幕和键盘则还是会分别自动熄屏和锁定。</p>
</blockquote>
<table>
<thead>
<tr>
<th style="text-align:center">标志常量</th>
<th style="text-align:center">CPU</th>
<th style="text-align:center">屏幕</th>
<th style="text-align:center">键盘</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">PARTIAL_WAKE_LOCK</td>
<td style="text-align:center">开</td>
<td style="text-align:center">关</td>
<td style="text-align:center">锁定</td>
</tr>
<tr>
<td style="text-align:center">SCREEN_DIM_WAKE_LOCK</td>
<td style="text-align:center">开</td>
<td style="text-align:center">低亮度</td>
<td style="text-align:center">锁定</td>
</tr>
<tr>
<td style="text-align:center">SCREEN_BRIGHT_WAKE_LOCK</td>
<td style="text-align:center">开</td>
<td style="text-align:center">高亮度</td>
<td style="text-align:center">锁定</td>
</tr>
<tr>
<td style="text-align:center">FULL_WAKE_LOCK</td>
<td style="text-align:center">开</td>
<td style="text-align:center">高亮度</td>
<td style="text-align:center">有效</td>
</tr>
</tbody>
</table>
<p>PS:本文参考此<a href="http://www.cnblogs.com/kobe8/p/3819305.html" target="_blank" rel="external">博文</a> ,在此表示感谢!</p>
]]></content>
<summary type="html">
<![CDATA[<p>Android系统下实现指定时长后执行某任务或周期性执行某项任务(以下统称为定时任务)通常有以下两种方式:1,定时任务实现TimerTask,Timer控制定时任务的启动,取消。2,通过PendingIntent执行定时任务,AlarmManager(安卓的闹钟服务)来控制定时任务的启动和取消。两者同样能实现,不同之处在于前者是Java范畴的定时任务,而Android官方推荐的定时任务实现则是后者。但是不一定完全遵照上述标准,特殊情况下还要特殊处理,比如这篇文章最后提到的问题。</p>]]>
</summary>
<category term="CPU休眠控制" scheme="http://jellybins.github.io/tags/CPU%E4%BC%91%E7%9C%A0%E6%8E%A7%E5%88%B6/"/>
<category term="定时任务" scheme="http://jellybins.github.io/tags/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/"/>
<category term="Android" scheme="http://jellybins.github.io/categories/Android/"/>
</entry>
<entry>
<title><![CDATA[终于等到你]]></title>
<link href="http://jellybins.github.io/2016/01/14/%E7%BB%88%E4%BA%8E%E7%AD%89%E5%88%B0%E4%BD%A0/"/>
<id>http://jellybins.github.io/2016/01/14/终于等到你/</id>
<published>2016-01-14T07:01:26.000Z</published>
<updated>2016-02-01T08:40:46.848Z</updated>
<content type="html"><![CDATA[<p>初看这篇博文的标题诸位一定以为我是要写一篇年度狗血秀恩爱的博文,但是考虑到我是一个理工科出身的人,最头疼的就是作文,所以事实上我是要讲一下这个静态博客的主题Next的修改问题,还有在配置Next主题时发现的主题配置文档里的部分问题。至于为什么要用这个标题呢,因为之前好多次讲要找个时间写一下,结果找个时间找到了现在,二零一六年元月,对于之前在知乎里询问我主题怎么修改成现在这样的亲们,真是灰常抱歉啊。说到修改其实我对nodejs几乎也是一窍不通,什么swig,styl也是根本从来没接触过,但好在有做Web开发的底子,外加上swig长的也比较像HTML加自有标签,还有神器firebug相助,然后摸索着修改了Next主题,最终完成了我的私人定制。</p>
<a id="more"></a>
<h2 id="拖延症的错">拖延症的错</h2><p>要写这篇博文前,突然想到先前搭建这个静态博客的时间是很久之前的事了,<a href="https://github.com/iissnan/hexo-theme-next" target="_blank" rel="external">Next主题</a> 不出意外的话,在现在这个时间节点肯定已经早就更新过n多次了(感谢作者的辛勤付出让我们用上这么漂亮简洁的主题),于是在本地新建了一个demo站点然后pull下来最新的主题,配置到这个示例站点上看了下,果不其然,作者又做出来一个Pisces的侧边栏分成双栏的scheme,非常漂亮,而且我所使用Mist的scheme作者也做了较大的改动,所以这次的主题修改以最新的Next主题的Mist scheme为例,最终完成的效果会与我的这个博客有些许不同,还请见谅。</p>
<h2 id="开始变(xiu)形(gai)了">开始变(xiu)形(gai)了</h2><h3 id="底栏部分">底栏部分</h3><ol>
<li><p>去除底部“由XXX驱动”文字及链接:找到站点(以deom为例)本地磁盘目录“demo\themes\next\layout_partials”文件夹下面的footer.swig文件,用记事本之类的文本编辑工具打开,删除以下两个div区块的内容。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"powered-by"</span>></span></span><br><span class="line">省略内容</span><br><span class="line"><span class="tag"></<span class="title">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"theme-info"</span>></span></span><br><span class="line">省略内容</span><br><span class="line"><span class="tag"></<span class="title">div</span>></span></span><br></pre></td></tr></table></figure>
</li>
<li><p>更改底栏高度:找到站点(以deom为例)本地磁盘目录“demo\themes\next\source\css_variables”文件夹下面的base.styl文件,打开后找到Layout sizes段落,修改如下内容。</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$footer</span>-<span class="attribute">height</span> = <span class="number">20px</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>更改底栏文字为居中对齐(侧边栏一直显示情况下比较美观,否则不用修改):找到站点(以deom为例)本地磁盘目录“demo\themes\next\source\css_scheme\Mist”文件夹下面的index.styl文件,打开后找到Layout sizes段落,修改如下内容。</p>
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class">.footer-inner</span> {</span><br><span class="line"> 省略内容</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="侧边栏部分">侧边栏部分</h3><ol>
<li><p>隐藏社交链接项下划线:找到站点(以deom为例)本地磁盘目录“demo\themes\next\source\css_common\components\siderbar”文件夹下面的sidebar-author-links.styl文件,打开后修改如下内容。</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class">.links-of-author</span> <span class="tag">a</span> {</span><br><span class="line"> 省略内容</span><br><span class="line"> <span class="attribute">border-bottom-color</span><span class="value">: <span class="variable">$black-deep</span>;</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>去除社交链接项前面的圆点:打开1步骤中同样文件去掉如下内容中标注的一行。</p>
<figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">.links-<span class="operator">of</span>-author <span class="operator">a</span> {</span><br><span class="line"> 省略内容</span><br><span class="line"> &:<span class="keyword">before</span> {</span><br><span class="line"> 省略内容</span><br><span class="line"> background: rgb(<span class="built_in">random</span>-color(<span class="number">0</span>, <span class="number">255</span>) - <span class="number">50</span>%,<span class="built_in">random</span>-color(<span class="number">0</span>, <span class="number">255</span>) - <span class="number">50</span>%, <span class="built_in">random</span>-color(<span class="number">0</span>, <span class="number">255</span>) - <span class="number">50</span>%);<span class="comment">#只去掉这一行#</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>去除社交链接文字描述:找到站点(以deom为例)本地磁盘目录“demo\themes\next\layout_macro”文件夹下面的sidebar.swig文件,打开后修改如下内容。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"links-of-author motion-element"</span>></span></span><br><span class="line"> 省略内容</span><br><span class="line"> {% if theme.social_icons.enable %}</span><br><span class="line"> <span class="tag"><<span class="title">i</span> <span class="attribute">class</span>=<span class="value">"fa fa-{{theme.social_icons[name] || 'globe' }}"</span>></span><span class="tag"></<span class="title">i</span>></span></span><br><span class="line"> {% endif %}</span><br><span class="line"><span class="tag"></<span class="title">div</span>></span></span><br></pre></td></tr></table></figure>
</li>
</ol>
<h2 id="主题配置中遇到的问题">主题配置中遇到的问题</h2><ol>
<li><p>启用社交链接时,别忘了把social前的注释关闭,在试用新版的主题时,因为这个生成静态页面时一直报错,最后发现是忘记把这个注释关闭了。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor"># Social links</span></span><br><span class="line"><span class="preprocessor"># social: <span class="comment">//这个前面的#号要去掉</span></span></span><br><span class="line"> <span class="preprocessor">#GitHub: <span class="comment">//去掉#号填写社交链接地址并且保留一个空格</span></span></span><br></pre></td></tr></table></figure>
</li>
<li><p>主题使用文档中“自定义logo及图标”部分现在已经废弃,但是有下面的问题其中步骤三中提到的新建styl文件的目录应该“source/css/_common/_fonts/”。</p>
</li>
<li><p>之前主题还存在一个文本上标(如数学公式中数字的平方的上标)、下标(如标识数字是什么进制的下标)显示不正常的问题,不知道最新的主题解决了没有,因为懒,我也没有再试验,也忘记当初老版本是怎么更改解决的了。</p>
</li>
</ol>
]]></content>
<summary type="html">
<![CDATA[<p>初看这篇博文的标题诸位一定以为我是要写一篇年度狗血秀恩爱的博文,但是考虑到我是一个理工科出身的人,最头疼的就是作文,所以事实上我是要讲一下这个静态博客的主题Next的修改问题,还有在配置Next主题时发现的主题配置文档里的部分问题。至于为什么要用这个标题呢,因为之前好多次讲要找个时间写一下,结果找个时间找到了现在,二零一六年元月,对于之前在知乎里询问我主题怎么修改成现在这样的亲们,真是灰常抱歉啊。说到修改其实我对nodejs几乎也是一窍不通,什么swig,styl也是根本从来没接触过,但好在有做Web开发的底子,外加上swig长的也比较像HTML加自有标签,还有神器firebug相助,然后摸索着修改了Next主题,最终完成了我的私人定制。</p>]]>
</summary>
<category term="Hexo" scheme="http://jellybins.github.io/tags/Hexo/"/>
<category term="Next主题" scheme="http://jellybins.github.io/tags/Next%E4%B8%BB%E9%A2%98/"/>
<category term="静态博客搭建" scheme="http://jellybins.github.io/tags/%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/"/>
<category term="杂文" scheme="http://jellybins.github.io/categories/%E6%9D%82%E6%96%87/"/>
</entry>
<entry>
<title><![CDATA[ActiveMQ中MQTT消息话题名符号映射问题]]></title>
<link href="http://jellybins.github.io/2015/10/14/ActiveMQ%E4%B8%ADMQTT%E6%B6%88%E6%81%AF%E8%AF%9D%E9%A2%98%E5%90%8D%E7%AC%A6%E5%8F%B7%E6%98%A0%E5%B0%84%E9%97%AE%E9%A2%98/"/>
<id>http://jellybins.github.io/2015/10/14/ActiveMQ中MQTT消息话题名符号映射问题/</id>
<published>2015-10-14T09:00:49.000Z</published>
<updated>2016-01-14T23:42:36.442Z</updated>
<content type="html"><![CDATA[<p>当使用ActiveMQ充当消息Broker时,如使用MQTT协议的客户端订阅发布ActiveMQ消息则会遇到一个ActiveMQ消息话题名和MQTT消息话题名映射的问题,在ActiveMQ官网的<a href="http://activemq.apache.org/mqtt.html" target="_blank" rel="external">MQTT支持文档</a> 中也提到了此问题,但是对于这个话题名符号映射在新老版本的ActiveMQ中的处理还是有差异存在,下面将详细说明。</p>
<a id="more"></a>
<h2 id="官方支持文档的说明">官方支持文档的说明</h2><h3 id="Working_with_Destinations_with_MQTT">Working with Destinations with MQTT</h3><hr>
<blockquote>
<p>MQTT supports hierarchies and wildcards, though the delimiters and characters are different: - Here’s the mapping:</p>
</blockquote>
<table>
<thead>
<tr>
<th style="text-align:center">function</th>
<th style="text-align:center">ActiveMQ</th>
<th style="text-align:center">MQTT</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">separator</td>
<td style="text-align:center">.</td>
<td style="text-align:center">/</td>
</tr>
<tr>
<td style="text-align:center">element</td>
<td style="text-align:center">*</td>
<td style="text-align:center">+</td>
</tr>
<tr>
<td style="text-align:center">sub tree</td>
<td style="text-align:center">></td>
<td style="text-align:center">#</td>
</tr>
</tbody>
</table>
<blockquote>
<p>These values are automatically transposed between clients using JMS/NMS/Stomp and clients using MQTT. For example - a client subscribing to “foo/#/bar” would receive messages published on a JMS Topic of foo.blah.bar.</p>
</blockquote>
<h3 id="蹩脚的翻译">蹩脚的翻译</h3><h2 id="使用MQTT协议的客户端工作">使用MQTT协议的客户端工作</h2><blockquote>
<p>MQTT支持层级分隔符和通配符,但是与传统ActiveMQ消息的不同:-这是映射表:</p>
</blockquote>
<table>
<thead>
<tr>
<th style="text-align:center">功能</th>
<th style="text-align:center">ActiveMQ</th>
<th style="text-align:center">MQTT</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">分隔符</td>
<td style="text-align:center">.</td>
<td style="text-align:center">/</td>
</tr>
<tr>
<td style="text-align:center">元素通配符</td>
<td style="text-align:center">*</td>
<td style="text-align:center">+</td>
</tr>
<tr>
<td style="text-align:center">子结构</td>
<td style="text-align:center">></td>
<td style="text-align:center">#</td>
</tr>
</tbody>
</table>
<blockquote>
<p>这些值(指上述分隔符和通配符)会在使用JMS/NMS/Stop协议的客户端和使用MQTT协议的客户端的消息传输过程中自动转换。例如-当一个采用MQTT协议的客户端订阅“foo/#/bar”这个话题的消息时将会收到以JMS话题“foo.blah.bar”发布的消息。</p>
</blockquote>
<h2 id="新旧版本ActiveMQ对上述映射处理的差异">新旧版本ActiveMQ对上述映射处理的差异</h2><p>正常情况下,在使用MQTT协议的客户端订阅发布消息话题用到上述分隔符和通配符的情况下,应该使用“/”、“+”和“#”这三个来订阅发布对应的JMS消息的话题名,如官方文档中提到的示例一样。在这种情况下,从不论是开始支持MQTT协议客户端的最早的ActiveMQ版本还是最新版本,上述映射没有任何差异。但是当使用MQTT协议的客户端订阅发布消息话题,使用的分隔符和通配符是“.”、“*”和“>”时,新旧版本的ActiveMQ对映射的处理却有如下不同。</p>
<p>以订阅话题foo.blah.bar为例:</p>
<hr>
<ol>
<li><p>ActiveMQ老版本(5.9.0及以下,5.9.X无法下载故未验证,理论上所有5.9版本均存在该问题)当MQTT客户端订阅的话题名为“foo.blah.bar”时,其实际订阅的ActiveMQ话题即为“foo.blah.bar”,即不对话题中的分隔符和通配符做映射转化。</p>
</li>
<li><p>ActiveMQ新版本(5.10.X版本),当MQTT客户端订阅的话题名为“foo.blah.bar”时,其实际订阅的ActiveMQ话题即为“foo\blah\bar”,即会对话题中的分隔符和通配符做映射转化。</p>
</li>
</ol>
<h2 id="综上及建议">综上及建议</h2><p>如果想消除ActiveMQ新老版本对消息话题中分隔符和通配符映射的差异,则最好采用官方推荐的方式,即MQTT的客户端订阅发布消息时话题中使用“/”、“+”和“#”等符号,JMS消息客户端订阅发布消息时话题中使用“.”、“*”和“>”等符号。</p>
]]></content>
<summary type="html">
<![CDATA[<p>当使用ActiveMQ充当消息Broker时,如使用MQTT协议的客户端订阅发布ActiveMQ消息则会遇到一个ActiveMQ消息话题名和MQTT消息话题名映射的问题,在ActiveMQ官网的<a href="http://activemq.apache.org/mqtt.html">MQTT支持文档</a> 中也提到了此问题,但是对于这个话题名符号映射在新老版本的ActiveMQ中的处理还是有差异存在,下面将详细说明。</p>]]>
</summary>
<category term="MQTT" scheme="http://jellybins.github.io/tags/MQTT/"/>
<category term="Topic" scheme="http://jellybins.github.io/tags/Topic/"/>
<category term="消息Broker" scheme="http://jellybins.github.io/tags/%E6%B6%88%E6%81%AFBroker/"/>
<category term="ActiveMQ" scheme="http://jellybins.github.io/categories/ActiveMQ/"/>
</entry>
<entry>
<title><![CDATA[路由器WPS的两种设置方式]]></title>
<link href="http://jellybins.github.io/2015/09/12/%E8%B7%AF%E7%94%B1%E5%99%A8WPS%E7%9A%84%E4%B8%A4%E7%A7%8D%E8%AE%BE%E7%BD%AE%E6%96%B9%E5%BC%8F/"/>
<id>http://jellybins.github.io/2015/09/12/路由器WPS的两种设置方式/</id>
<published>2015-09-12T08:09:11.000Z</published>
<updated>2015-09-12T09:07:10.705Z</updated>
<content type="html"><![CDATA[<p>最近趁着京东搞活动,终于以历史底价入了心仪已久的美国网件路由器WNDR4300,之所以选中它,是因为它的可折腾性极高,不过入手后也没有着急折腾,先体验了下官方固件到底怎么样。然后一通配置之后,发现一个问题,就是我取消了启用路由器PIN这个选项的勾选,路由器上WPS按键旁边的灯却依然亮着无法关闭。之所以纠结这个是因为之前曾利用PIN码破解过几个路由器,感觉这是一个极不靠谱的东西,必须把它关掉。后来查看路由器的设置说明,WPS灯正常就是常亮状态,即使禁用了PIN功能,然后今天在网上又查询关于关闭WPS功能的方法的时候,突然发现了我又一个自以为是的错误想法,下面慢慢道来,说不定你也有这个失误。</p>
<a id="more"></a>
<h2 id="什么是WPS?">什么是WPS?</h2><blockquote>
<p>WPS(Wi-Fi Protected Setup,Wi-Fi保护设置)(有的叫做AOSS、有的叫做QSS,不过功能都一致。)是由Wi-Fi联盟组织实施的认证项目,主要致力于简化无线局域网的安装及安全性能配置工作。在传统方式下,用户新建一个无线网络时,必须在接入点手动设置网络名(SSID)和安全密钥,然后在客户端验证密钥以阻止“不速之客”的闯入。这整个过程需要用户具备Wi-Fi设备的背景知识和修改必要配置的能力。Wi- Fi Protected Setup能帮助用户自动设置网络名(SSID)、配置强大的WPA数据编码及认证功能,用户只需输入个人信息码(PIN方法)或按下按钮(按钮设置,或称PBC),即能安全地连入WLAN。这大大简化了无线安全设置的操作。Wi-Fi Protected Setup支持多种通过Wi-Fi认证的802.11产品,包括接入点、无线适配器、Wi-Fi电话以及其他消费性电子设备。</p>
<p>WPS可以帮助客户端用户自动配置网络名(SSID)及无线加密密钥。对于普通用户来说,无需了解SSID和安全密钥的概念就能实现安全连接;而且用户的安全密钥不可能被外人破解,因为它是随机产生的;最重要的是用户无需记忆冗长的无线加密密码,避免了忘记密码的麻烦。</p>
</blockquote>
<h2 id="我的失误在哪里?">我的失误在哪里?</h2><p>从上面百度百科对WPS的解释中,我发现我的失误在于一直把WPS的设置方式理解成了只要取消PIN方式,WPS功能即关闭了。其实我有这种理解也是因为平时接触的低端路由器,好像就是这样设计的,直到接触到这个网件的路由器,然后又碰到了WPS灯无法关闭的问题才发现了这个问题。事实上WPS设置有PBC(BUTTON)按键和PIN码输入两种设置方法,其实我想关闭的就是那个不靠谱的通过PIN码输入实现WPS连接的方法,而不是整个WPS功能,说到这里还是老外的东西分的清楚,不像兲朝的路由器厂家WPS一关全关。</p>
<h2 id="详解两种设置方式">详解两种设置方式</h2><h3 id="PBC:_按WPS按钮实现WPS安全连接。">PBC: 按WPS按钮实现WPS安全连接。</h3><ol>
<li>在AP中,在WPS设置中,设置为启用。</li>
<li>按一下客户端(无线网卡)上的WPS按键,搜索WPS网络。</li>
<li>按一下AP上的WPS按键,WPS开始链接协商,片刻后WPS安全连接成功建立。</li>
</ol>
<h3 id="PIN:输入PIN码实现WPS安全连接(分两小种)。">PIN:输入PIN码实现WPS安全连接(分两小种)。</h3><ul>
<li>PIN(Internal Register,相对于AP而言):通过在路由器中输入客户端PIN码来实现WPS安全连接。</li>
</ul>
<ol>
<li><p>在WPS设置中,把状态设置为启用。</p>
</li>
<li><p>打开客户端WPS设置软件,选择在路由器中输入PIN的方式连接,同时软件上还会显示客户端当前的PIN码。</p>
</li>
<li><p>打开路由器界面,在WPS模式里选择PIN模式,然后输入客户端的PIN码,点添加新设备,一会儿后,WPS安全连接成功建立。</p>
</li>
</ol>
<ul>
<li>PIN(External Register,相对于AP而言):通过输入AP的PIN码实现WPS安全连接。</li>
</ul>
<ol>
<li><p>在AP中,在WPS设置中,设置为启用。</p>
</li>
<li><p>记住AP的PIN码,然后打开客户端(无线网卡)WPS设置软件,选择以AP的PIN码来进行连接。</p>
</li>
<li><p>输入完PIN码后,点下一步,一会儿后,WPS安全连接成功建立。</p>
</li>
</ol>
]]></content>
<summary type="html">
<![CDATA[<p>最近趁着京东搞活动,终于以历史底价入了心仪已久的美国网件路由器WNDR4300,之所以选中它,是因为它的可折腾性极高,不过入手后也没有着急折腾,先体验了下官方固件到底怎么样。然后一通配置之后,发现一个问题,就是我取消了启用路由器PIN这个选项的勾选,路由器上WPS按键旁边的灯却依然亮着无法关闭。之所以纠结这个是因为之前曾利用PIN码破解过几个路由器,感觉这是一个极不靠谱的东西,必须把它关掉。后来查看路由器的设置说明,WPS灯正常就是常亮状态,即使禁用了PIN功能,然后今天在网上又查询关于关闭WPS功能的方法的时候,突然发现了我又一个自以为是的错误想法,下面慢慢道来,说不定你也有这个失误。</p>]]>
</summary>
<category term="PIN" scheme="http://jellybins.github.io/tags/PIN/"/>
<category term="WPS" scheme="http://jellybins.github.io/tags/WPS/"/>
<category term="路由器" scheme="http://jellybins.github.io/tags/%E8%B7%AF%E7%94%B1%E5%99%A8/"/>
<category term="网络" scheme="http://jellybins.github.io/categories/%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title><![CDATA[Linux错误和标准输出重定向]]></title>
<link href="http://jellybins.github.io/2015/04/12/Linux%E9%94%99%E8%AF%AF%E5%92%8C%E6%A0%87%E5%87%86%E8%BE%93%E5%87%BA%E9%87%8D%E5%AE%9A%E5%90%91/"/>
<id>http://jellybins.github.io/2015/04/12/Linux错误和标准输出重定向/</id>
<published>2015-04-12T07:18:20.000Z</published>
<updated>2016-06-14T07:32:04.287Z</updated>
<content type="html"><![CDATA[<p>例:/dev/null 2>&1 这句话的含义及使用的意义?</p>
<p>一般在linux的shell包括windows的dos下都可能会这样使用。null是一个名叫null小桶的东西,将输出重定向到它的好处是不会因为输出的内容过多而导致文件大小不断的增加。其实,可以把null认为是什么都没有,也就是将命令的输出抛弃掉。</p>
<p>1表示标准输出,2表示标准错误输出,2>&1表示将标准错误输出重定向到标准输出,这样,程序或者命令的正常输出和错误输出就可以在标准输出中输出。</p>
<a id="more"></a>
<p>一般来讲,标准输出和标准错误输出都是屏幕,那为什么还要这么用呢?原因是标准输出的重定向。例子是重定向到了null,如果重定向到文件则为<code>dir > out.txt</code>,表示标准输出重定向到out.txt文件。此时如果dir命令出错,那么错误信息不会输出到out.txt文件,错误信息仍然会输出到屏幕—标准错误输出。为了使正确的信息和错误的信息都重定向到out.txt文件,那么需要将错误信息的标准错误输出重定向到标准输出。即命令如下:<code>dir > out.txt 2>&1</code>,重定向到null是一个道理:<code>dir > null 2>&1</code>。</p>
]]></content>
<summary type="html">
<![CDATA[<p>例:/dev/null 2>&1 这句话的含义及使用的意义?</p>
<p>一般在linux的shell包括windows的dos下都可能会这样使用。null是一个名叫null小桶的东西,将输出重定向到它的好处是不会因为输出的内容过多而导致文件大小不断的增加。其实,可以把null认为是什么都没有,也就是将命令的输出抛弃掉。</p>
<p>1表示标准输出,2表示标准错误输出,2>&1表示将标准错误输出重定向到标准输出,这样,程序或者命令的正常输出和错误输出就可以在标准输出中输出。</p>]]>
</summary>
<category term="脚本语法" scheme="http://jellybins.github.io/tags/%E8%84%9A%E6%9C%AC%E8%AF%AD%E6%B3%95/"/>
<category term="重定向" scheme="http://jellybins.github.io/tags/%E9%87%8D%E5%AE%9A%E5%90%91/"/>
<category term="Linux" scheme="http://jellybins.github.io/categories/Linux/"/>
</entry>
<entry>
<title><![CDATA[王自如罗永浩舌战观后感]]></title>
<link href="http://jellybins.github.io/2014/08/28/%E7%8E%8B%E8%87%AA%E5%A6%82%E7%BD%97%E6%B0%B8%E6%B5%A9%E8%88%8C%E6%88%98%E8%A7%82%E5%90%8E%E6%84%9F/"/>
<id>http://jellybins.github.io/2014/08/28/王自如罗永浩舌战观后感/</id>
<published>2014-08-28T14:11:24.000Z</published>
<updated>2016-05-31T03:42:50.399Z</updated>
<content type="html"><![CDATA[<p>又花了一个小时把老罗和王自如的舌战后半部分看完了。不能否认老罗的嘴确实很屌。但也可以说狠毒。姜还是老的辣啊。本身对王自如了解不多。而且在前两个小时的舌战里甚至感觉老罗摆事实讲道理靠谱。但是这最后的一个小时看到的却是各种变相的人身攻击。各种讽刺。极尽挖苦之能事,完全脱离了这次舌战的中心产品。再到最后假模假式的解释自己失态道歉。就类似于我故意把你打个半死。再跟你道歉说我没控制好一样。这才是真正的耍流氓。再有回味整个舌战。其实也完全可以理清老罗的应对策略。,你说我不好,我拿苹果三星说他跟我一样。二,你说我不好,我抛开苹果三星,我跟大多厂商一样。三,你说我不好,我说你人有问题。四,你说我不好,我揪住你人不好和一个之前的测评的败笔。五,你说我不好,我管你说我好不好,我跟观众讲,让观众去想。这五点够搞笑吧。对他们两个人没有什么支持谁,我绝对是独立,客观,第三方,就像老罗提到的手机摆放角度影响屏幕颜色问题,先不说王自如是不是故意为之,这就是他的错,错了就是错了,再对于王自如提到手机色温,动态对比度问题,老罗始终以已达到行业普通水平解释,那之前你鼓吹的工匠情怀呢,你的不在乎输赢,就是认真呢,你的东半球最屌手机呢,怎么不提了。还有提到王自如发布会打理想主义者的旗号却变相做妥协的事,你何尝又不是拿着情怀来忽悠人。视频最后老罗又以机票酒店费用讽刺对方,这是最让我恶心的一个场景,这个让老罗看起来是最后的赢家的场景。但是有时候,表面的赢其实是真正的输。最后,站在普通观众角度感谢王刘(zi)翔(ru)和杜(luo)海(yong)涛(hao)带来的狗咬狗狗血脱口秀表演,我想说我爱这个世界,偶尔也爱狗血。</p>
]]></content>
<summary type="html">
<![CDATA[<p>又花了一个小时把老罗和王自如的舌战后半部分看完了。不能否认老罗的嘴确实很屌。但也可以说狠毒。姜还是老的辣啊。本身对王自如了解不多。而且在前两个小时的舌战里甚至感觉老罗摆事实讲道理靠谱。但是这最后的一个小时看到的却是各种变相的人身攻击。各种讽刺。极尽挖苦之能事,完全脱离了这次]]>
</summary>
<category term="锤子" scheme="http://jellybins.github.io/tags/%E9%94%A4%E5%AD%90/"/>
<category term="杂文" scheme="http://jellybins.github.io/categories/%E6%9D%82%E6%96%87/"/>
</entry>
<entry>
<title><![CDATA[SQLServer创建登录用户错误]]></title>
<link href="http://jellybins.github.io/2014/08/17/SQLServer%E5%88%9B%E5%BB%BA%E7%99%BB%E5%BD%95%E7%94%A8%E6%88%B7%E9%94%99%E8%AF%AF/"/>
<id>http://jellybins.github.io/2014/08/17/SQLServer创建登录用户错误/</id>
<published>2014-08-17T09:42:19.000Z</published>
<updated>2015-08-20T02:39:29.052Z</updated>
<content type="html"><![CDATA[<h2 id="错误描述">错误描述</h2><p>在Microsoft SQLServer 还原数据库后,要创建登录该数据库的用户结果提示“错误15023:当前数据库中已存在用户或角色”或“用户、组或角色 在当前数据库中已存在”。</p>
<h2 id="原因及解决办法">原因及解决办法</h2><ul>
<li>原因:<br>SQLServer中登录用户名与数据库用户用户名的区别。登录用户名用于用户身份验证,而数据库用户用户名用于数据库访问和权限验证。登录用户通过安全识别符 (SID) 与数据库用户关联。将数据库恢复到其他服务器时,数据库中包含一组用户和权限,但可能没有相应的登录用户或者登录所关联的用户可能不是相同的用户。这种情况被称为存在“孤立用户”。</li>
<li>解决办法:<br>调用系统存储过程sp_change_users_login,具体用法如下:<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">use</span> 数据库名</span><br><span class="line"><span class="keyword">go</span></span><br><span class="line">sp_change_users_login <span class="string">'update_one'</span>, <span class="string">'用户名'</span>,<span class="string">'用户名'</span></span></span><br></pre></td></tr></table></figure>
</li>
</ul>
<blockquote>
<p>其中数据库名为存在孤立用户的数据库,update_one是存储过程的参数,表示只处理一个用户,前一个用户名是数据库用户,后一个用户名是用来数据库登录,以上这个SQL命令表示将数据库用户用户名与服务器登录用户名重新连接起来。这样就可以正常使用数据库了。</p>
</blockquote>
]]></content>
<summary type="html">
<![CDATA[<h2 id="错误描述">错误描述</h2><p>在Microsoft SQLServer 还原数据库后,要创建登录该数据库的用户结果提示“错误15023:当前数据库中已存在用户或角色”或“用户、组或角色 在当前数据库中已存在”。</p>
<h2 id="原因及解决办法">原因]]>
</summary>
<category term="SQLServer" scheme="http://jellybins.github.io/tags/SQLServer/"/>
<category term="创建用户错误" scheme="http://jellybins.github.io/tags/%E5%88%9B%E5%BB%BA%E7%94%A8%E6%88%B7%E9%94%99%E8%AF%AF/"/>
<category term="数据库" scheme="http://jellybins.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
<entry>
<title><![CDATA[SQL视图、子查询中使用ORDER BY报错]]></title>
<link href="http://jellybins.github.io/2014/07/15/SQL%E8%A7%86%E5%9B%BE%E3%80%81%E5%AD%90%E6%9F%A5%E8%AF%A2%E4%B8%AD%E4%BD%BF%E7%94%A8ORDER-BY%E6%8A%A5%E9%94%99/"/>
<id>http://jellybins.github.io/2014/07/15/SQL视图、子查询中使用ORDER-BY报错/</id>
<published>2014-07-15T06:57:59.000Z</published>
<updated>2016-06-14T07:39:54.849Z</updated>
<content type="html"><![CDATA[<p>默认情况下,如果在子查询,函数,视图中尝试去使用ORDER BY,如:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">select</span> * <span class="keyword">from</span> (<span class="keyword">select</span> * <span class="keyword">from</span> tab <span class="keyword">where</span> ID><span class="number">20</span> <span class="keyword">order</span> <span class="keyword">by</span> userID <span class="keyword">desc</span>) <span class="keyword">as</span> a <span class="keyword">order</span> <span class="keyword">by</span> <span class="built_in">date</span> <span class="keyword">desc</span></span></span><br></pre></td></tr></table></figure>
<p>可能会遇到下面的错误提示:</p>
<blockquote>
<p>除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效。</p>
</blockquote>
<ul>
<li>上述错误产生的原因:针对一个表的SELECT其实并不是返回一个表,而是一个游标。如果一定要使用,则需要配合<code>TOP 100 PERCENT</code>使用:</li>
</ul>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">select</span> * <span class="keyword">from</span> (<span class="keyword">select</span> top <span class="number">100</span> percent * <span class="keyword">from</span> tab <span class="keyword">where</span> ID><span class="number">20</span> <span class="keyword">order</span> <span class="keyword">by</span> userID <span class="keyword">desc</span>) <span class="keyword">as</span> a <span class="keyword">order</span> <span class="keyword">by</span> <span class="built_in">date</span> <span class="keyword">desc</span></span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<![CDATA[<p>默认情况下,如果在子查询,函数,视图中尝试去使用ORDER BY,如:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><]]>
</summary>
<category term="SQL语法" scheme="http://jellybins.github.io/tags/SQL%E8%AF%AD%E6%B3%95/"/>
<category term="数据库" scheme="http://jellybins.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
<entry>
<title><![CDATA[SQL中and与or优先级比较]]></title>
<link href="http://jellybins.github.io/2014/06/14/SQL%E4%B8%ADand%E4%B8%8Eor%E4%BC%98%E5%85%88%E7%BA%A7%E6%AF%94%E8%BE%83/"/>
<id>http://jellybins.github.io/2014/06/14/SQL中and与or优先级比较/</id>
<published>2014-06-14T06:49:34.000Z</published>
<updated>2016-06-14T07:07:58.872Z</updated>
<content type="html"><![CDATA[<p>sql 语句中 and 和or的优先级比较</p>
<p>例:<br>select <em> from test where condition1 or condition2 and condition3;<br>其执行效果与下面的sql等价:<br>select </em> from test where condition1 or (condition2 and condition3);</p>
<blockquote>
<p>故可以得出结论:and级别高于or</p>
</blockquote>
<p>多个and 和 or 混用也是这个道理,相当于把 and 看成乘号(*), 把or看成加号(+),这样sql的执行顺序就一目了然了。</p>
<p>最后还是建议大家严格按照业务逻辑写sql,必要的时候毫不吝啬使用括号,就像上面的情况,如果逻辑是:condition1 且 condition2 或者 condition3,那么请写成:<code>select * from test where (condition1 and condition2) or condition3</code>这样既不会出错,也方便以后查看代码。</p>
]]></content>
<summary type="html">
<![CDATA[<p>sql 语句中 and 和or的优先级比较</p>
<p>例:<br>select <em> from test where condition1 or condition2 and condition3;<br>其执行效果与下面的sql等价:<br>select </em]]>
</summary>
<category term="SQL语法" scheme="http://jellybins.github.io/tags/SQL%E8%AF%AD%E6%B3%95/"/>
<category term="运算符优先级" scheme="http://jellybins.github.io/tags/%E8%BF%90%E7%AE%97%E7%AC%A6%E4%BC%98%E5%85%88%E7%BA%A7/"/>
<category term="数据库" scheme="http://jellybins.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
<entry>
<title><![CDATA[再谈线程synchronized同步]]></title>
<link href="http://jellybins.github.io/2013/10/20/%E5%86%8D%E8%B0%88%E7%BA%BF%E7%A8%8Bsynchronized%E5%90%8C%E6%AD%A5/"/>
<id>http://jellybins.github.io/2013/10/20/再谈线程synchronized同步/</id>
<published>2013-10-20T08:59:20.000Z</published>
<updated>2015-09-12T01:39:54.832Z</updated>
<content type="html"><![CDATA[<p><code>synchronized</code>是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。</p>
<h2 id="先摆结论">先摆结论</h2><ol>
<li><p>当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。</p>
</li>
<li><p>然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。</p>
</li>
<li><p>尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。</p>
</li>
<li><p>第三个例子同样适用其它同步方法。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步方法的访问都被暂时阻塞。</p>
</li>
<li><p>以上规则对其它对象锁同样适用。</p>
</li>
</ol>
<a id="more"></a>
<h2 id="再来举例">再来举例</h2><h3 id="代码示例1">代码示例1</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Thread1</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName()</span><br><span class="line"> + <span class="string">" synchronized loop "</span> + i);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> Thread1 t1 = <span class="keyword">new</span> Thread1();</span><br><span class="line"> Thread ta = <span class="keyword">new</span> Thread(t1, <span class="string">"A"</span>);</span><br><span class="line"> Thread tb = <span class="keyword">new</span> Thread(t1, <span class="string">"B"</span>);</span><br><span class="line"> ta.start();</span><br><span class="line"> tb.start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>A synchronized loop 0<br>A synchronized loop 1<br>A synchronized loop 2<br>A synchronized loop 3<br>A synchronized loop 4<br>B synchronized loop 0<br>B synchronized loop 1<br>B synchronized loop 2<br>B synchronized loop 3<br>B synchronized loop 4</p>
<p>当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。</p>
</blockquote>
<h3 id="代码示例2">代码示例2</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Thread2</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">m4t1</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) {</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName()</span><br><span class="line"> + <span class="string">" : "</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">m4t2</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" : "</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">final</span> Thread2 myt2 = <span class="keyword">new</span> Thread2();</span><br><span class="line"> Thread t1 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> myt2.m4t1();</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"t1"</span>);</span><br><span class="line"> Thread t2 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> myt2.m4t2();</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"t2"</span>);</span><br><span class="line"> t1.start();</span><br><span class="line"> t2.start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>t1 : 4<br>t2 : 4<br>t1 : 3<br>t2 : 3<br>t1 : 2<br>t2 : 2<br>t1 : 1<br>t2 : 1<br>t1 : 0<br>t2 : 0</p>
<p>t1线程访问Thread2的一个synchronized(this)同步代码块m4t1时,t2线程仍然可以访问该Thread2中的非synchronized(this)同步代码块m4t2。</p>
</blockquote>
<h3 id="代码示例3">代码示例3</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//修改代码示例2中Thread2.m4t2()方法: </span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">m4t2</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) {</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" : "</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>t1 : 4<br>t1 : 3<br>t1 : 2<br>t1 : 1<br>t1 : 0<br>t2 : 4<br>t2 : 3<br>t2 : 2<br>t2 : 1<br>t2 : 0</p>
<p>t1线程访问Thread2的一个synchronized(this)同步代码块m4t1时,t2线程对Thread2中所有其它synchronized(this)同步代码块m4t2的访问被阻塞。</p>
</blockquote>
<h3 id="代码示例4">代码示例4</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//修改Thread2.m4t2()方法如下:</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">m4t2</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" : "</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>t1 : 4<br>t1 : 3<br>t1 : 2<br>t1 : 1<br>t1 : 0<br>t2 : 4<br>t2 : 3<br>t2 : 2<br>t2 : 1<br>t2 : 0</p>
<p>t1线程访问Thread2的一个synchronized(this)同步代码块m4t1时,它就获得了这个Thread2的对象锁。结果,t2线程对该Thread2对象所有同步方法的访问都被暂时阻塞,即3的规则对同步方法也适用。</p>
</blockquote>
<h3 id="代码示例5">代码示例5</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Thread3</span> </span>{</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Inner</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">m4t1</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName()</span><br><span class="line"> + <span class="string">" : Inner.m4t1()="</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">m4t2</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName()</span><br><span class="line"> + <span class="string">" : Inner.m4t2()="</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">m4t1</span><span class="params">(Inner inner)</span> </span>{ </span><br><span class="line"> <span class="keyword">synchronized</span>(inner) { <span class="comment">//使用对象锁 </span></span><br><span class="line"> inner.m4t1(); </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">m4t2</span><span class="params">(Inner inner)</span> </span>{</span><br><span class="line"> inner.m4t2();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">final</span> Thread3 myt3 = <span class="keyword">new</span> Thread3();</span><br><span class="line"> <span class="keyword">final</span> Inner inner = myt3.new Inner();</span><br><span class="line"> Thread t1 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> myt3.m4t1(inner);</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"t1"</span>);</span><br><span class="line"> Thread t2 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> myt3.m4t2(inner);</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"t2"</span>);</span><br><span class="line"> t1.start();</span><br><span class="line"> t2.start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>t1 : Inner.m4t1()=4<br>t2 : Inner.m4t2()=4<br>t1 : Inner.m4t1()=3<br>t2 : Inner.m4t2()=3<br>t1 : Inner.m4t1()=2<br>t2 : Inner.m4t2()=2<br>t1 : Inner.m4t1()=1<br>t2 : Inner.m4t2()=1<br>t1 : Inner.m4t1()=0<br>t2 : Inner.m4t2()=0</p>
<p>尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。</p>
</blockquote>
<p>现在在Inner.m4t2()前面加上synchronized:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//修改Inner.m4t2()方法如下:</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">m4t2</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">while</span> (i-- > <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName()</span><br><span class="line"> + <span class="string">" : Inner.m4t2()="</span> + i);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">500</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>t1 : Inner.m4t1()=4<br>t1 : Inner.m4t1()=3<br>t1 : Inner.m4t1()=2<br>t1 : Inner.m4t1()=1<br>t1 : Inner.m4t1()=0<br>t2 : Inner.m4t2()=4<br>t2 : Inner.m4t2()=3<br>t2 : Inner.m4t2()=2<br>t2 : Inner.m4t2()=1<br>t2 : Inner.m4t2()=0</p>
<p>尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2对Inner.m4t2()的访问也被阻塞,因为m4t2()是Inner中的一个同步方法。</p>
</blockquote>
]]></content>
<summary type="html">
<![CDATA[<p><code>synchronized</code>是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。</p>
<h2 id="先摆结论">先摆结论</h2><ol>
<li><p>当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。</p>
</li>
<li><p>然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。</p>
</li>
<li><p>尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。</p>
</li>
<li><p>第三个例子同样适用其它同步方法。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步方法的访问都被暂时阻塞。</p>
</li>
<li><p>以上规则对其它对象锁同样适用。</p>
</li>
</ol>]]>
</summary>
<category term="线程" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B/"/>
<category term="线程同步" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/"/>
<category term="Java" scheme="http://jellybins.github.io/categories/Java/"/>
</entry>
<entry>
<title><![CDATA[线程的结束,挂起和恢复]]></title>
<link href="http://jellybins.github.io/2013/10/11/%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%BB%93%E6%9D%9F%EF%BC%8C%E6%8C%82%E8%B5%B7%E5%92%8C%E6%81%A2%E5%A4%8D/"/>
<id>http://jellybins.github.io/2013/10/11/线程的结束,挂起和恢复/</id>
<published>2013-10-11T06:26:22.000Z</published>
<updated>2015-09-17T09:29:08.524Z</updated>
<content type="html"><![CDATA[<h2 id="引言">引言</h2><p>Android的车辆监控端应用开发中,碰到了轨迹回放控制,播放、暂停、停止不知道该怎么实现的问题,虽然知道要用线程。然后找到了这篇文章,讲解的非常清晰,在此记录之,文末有原文链接。最后将整个轨迹回放实现完成后,再看代码恍然大悟,这不就是音乐播放器的播放、暂停、停止的实现,当初看播放器源码应该很容易就解决了,结果重复造了轮子,不过还是很有收获的。</p>
<p>PS:程序示例部分是我在原作者的基础上修改而来,感觉应该更通俗易懂些,希望能对需要的人有帮助,有什么错误的话还请指正,谢谢。</p>
<a id="more"></a>
<h2 id="Android中的线程操作">Android中的线程操作</h2><p>在Android应用的开发过程中,我们有时候需要通过创建一个新的线程去完成一些任务。例如,我们去进行搜寻动作,如果搜寻比较费时,我们就需要通过进度条来提示用户搜寻的进展情况,避免用户认为发生了死机。此时进度条的刷新就需要另外一个线程去实现。</p>
<p>但是这里有一个误区:有些人在多线程开发的时候会错误的认为,如果我们从创建线程的Activity中退出(该Acitivity被销毁),则在该Activity中创建的自定义线程也会被销毁。其实这是大错特错了。</p>
<p>实践证明,上述情况下,创建的线程并不会自动销毁,而是仍然在后台默默无闻地执行,直到自行结束。Android的这种设计是无可厚非的。从理论上来解释,应用的最小执行单位是线程,最小资源单位是进程,一个进程可以包含多个线程,而多个线程共享同一个所属进程的资源。因此,个人理解Android的应用其实就是一个进程,而里面的每个UI,Activity就是从属这个进程的线程,从一个Activity进入另外一个Activity本质就是将之前的线程挂起,然后创建后面的线程。退出也是同理。自定义线程也是遵循这个原则的。除非去控制某个线程结束,否则只有当该现程执行完毕或者所属的进程被销毁,该线程才会真正的结束。</p>
<p>综上,当我们在自定义线程还没有执行完毕的情况下,需要结束相关动作的时候,我们就要认为地去结束相关线程。例如,在搜寻过程中,我们不想去继续搜寻,而退出了搜寻功能,此时我们就需要去结束自定义的搜寻线程。如果不这样会可能造成严重错误。例如,我们反复进入搜寻功能去搜寻,在搜寻未结束时退出,然后再进入。这种情况下,由于之前的自定义线程并未结束,而之后又会有多个新搜寻线程被创建执行,很容易导致临界区冲突,从而导致设备当机。</p>
<p>那么我们如何控制这些自定义线程呢?</p>
<h2 id="线程的结束">线程的结束</h2><p>其实,通过帮助文档,我们可以知道,Android的线程类本身就提供了一些公共方法去结束线程。</p>
<blockquote>
<p><strong>void destroy()</strong></p>
<p>This method is deprecated. Not implemented. </p>
<p><strong>synchronized final void stop(Throwablethrowable)</strong></p>
<p>This method is deprecated. because stopping a thread in this manner is unsafe and can leave your application and the VM in an unpredictable state. </p>
<p><strong>final void stop()</strong></p>
<p>This method is deprecated. because stopping a thread in this manner is unsafe and can leave your application andthe VM in an unpredictable state. </p>
</blockquote>
<p>但是,通过说明我们可以看到,这些方法Android本身都是不推荐使用的,通过这种方式结束线程是不安全的,可能会让我们的应用退出,并且会让虚拟机处于一种无法预料的状态。那么开发过程中,在合情合理的需求中,我们怎么去安全的结束指定的自定义线程呢?</p>
<h3 id="解决方法">解决方法</h3><ol>
<li><p>我们可以在自定义线程类中定义一个布尔私有变量,并且初始化为假,用于记录线程的执行状态。</p>
</li>
<li><p>在run函数开始,设置该变量为真,表示线程进入执行状态。</p>
</li>
<li><p>在run函数结束位置,设置该变量为假,表示线程进入结束状态。</p>
</li>
<li><p>在run的线程执行部分,我们可以找一些锲点,对该变量进行判断,如果为真则继续执行,否则退出run函数。</p>
</li>
<li><p>在自定义线程类中再提供一个公共函数,该函数的作用是将上述状态变量设置为假。</p>
</li>
</ol>
<blockquote>
<p>这样,当自定义线程执行还未结束时,我们就可以通过调用5中的方法将线程安全结束。思想的本质就是,既然我们不能安全强制结束线程,那我们就让它安全地提前退出。效果是一样的。</p>
</blockquote>
<h3 id="程序实例">程序实例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SearchThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">boolean</span> isRun = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">SearchThread</span><span class="params">()</span> </span>{</span><br><span class="line"> isRun = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setStopState</span><span class="params">()</span> </span>{</span><br><span class="line"> isRun = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//改变为运行状态</span></span><br><span class="line"> isRun = <span class="keyword">true</span>;</span><br><span class="line"> <span class="comment">//开始运行</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</span><br><span class="line"> <span class="comment">//每次运行检查运行标志位</span></span><br><span class="line"> <span class="keyword">if</span> (!isRun) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// <span class="doctag">TODO</span> Something</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="线程的挂起与恢复">线程的挂起与恢复</h2><p>同样,Android线程类也提供了一些公共方法去挂起和恢复线程:</p>
<blockquote>
<p><strong>final void resume()</strong></p>
<p>This method is deprecated. Used with deprecated method suspend.</p>
<p><strong>final void suspend()</strong></p>
<p>This method is deprecated. May cause deadlocks.</p>
</blockquote>
<p>同样不幸的是,通过说明我们可以看到,这些方法Android也是不推荐使用的,经过笔者试验,这些方法也没有效果。那我们如何去挂起和恢复线程呢?</p>
<p>Android的类基本都是继承于Object类。此类可以说是Android类的祖先了。如果把Android类比喻为一个生物界,则Object类就是当初生命诞生时候的那个单细胞。</p>
<p>我们可以发现Object类提供了几个方法:</p>
<blockquote>
<p><strong>final void notify()</strong></p>
<p>Causes a thread which is waiting on this object’smonitor (by means of calling one of the wait() methods) to be woken.</p>
<p><strong>final void notifyAll()</strong></p>
<p>Causes all threads which are waiting on thisobject’s monitor (by means of calling one of the wait() methods) to be woken.</p>
<p><strong>final void wait()</strong></p>
<p>Causes the calling thread to wait until another thread calls the notify() or notifyAll() method of this object.</p>
</blockquote>
<p>通过说明我们可以知道,wait方法可以让正在调用的线程处于等待状态,直到其他线程调用了该对象的notify或者notifyAll,而notify和notifyAll方法则是用于唤醒处于等待中的线程。</p>
<p>同样,线程类也是继承于Object类,但是线程类是一个比较特殊的类,有自己独立的栈机制来处理其方法,参数和局部变量。通过实验发现,虽然线程类继承于Object类,但是却不能通过wait和notify方法挂起唤醒线程。而要实现上述动作,必须去间接地实现,即在自定义线程类中创建一个Object对象,然后通过对该Object的相关操作实现线程的挂起和唤醒。</p>
<h3 id="解决方法-1">解决方法</h3><ol>
<li><p>在自定义线程类的实例化过程中创建一个Object对象。</p>
</li>
<li><p>定义一个变量来记录线程的状态是否挂起,初始化为假。</p>
</li>
<li><p>在线程类中的run函数中的线程执行部分找入锲点来执行下面动作:如果当前状态变量为假(表示线程挂起),则通过1中Object对象的wait方法挂起当前线程,即线程暂停在锲点位置,如果被唤起,则将从锲点后继续执行。</p>
</li>
<li><p>定义一个方法来修改线程状态变量为真,从而达到挂起线程的目的。</p>
</li>
<li><p>定义一个方法去唤醒线程。判断如果线程状态变量为真,则修改其为假,然后调用1中Object对象的notifyAll方法唤醒对象。(notify方法也可以,但是如果自定义线程较多时容易造成死锁)。</p>
</li>
</ol>
<blockquote>
<p>综上,当自定义线程运行后我们可以通过4中的方法挂起线程,通过5中的方法唤醒线程。</p>
</blockquote>
<h3 id="程序实例-1">程序实例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SearchThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Object mPauseLock;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">boolean</span> mPauseFlag;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">SearchThread</span><span class="params">()</span> </span>{</span><br><span class="line"> mPauseLock = <span class="keyword">new</span> Object();</span><br><span class="line"> mPauseFlag = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">pauseThread</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (mPauseLock) {</span><br><span class="line"> mPauseFlag = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">resumeThread</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (mPauseLock) {</span><br><span class="line"> mPauseFlag = <span class="keyword">false</span>;</span><br><span class="line"> mPauseLock.notifyAll();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">//线程开始执行</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</span><br><span class="line"> <span class="comment">//每次执行检测是否应改变为暂停状态</span></span><br><span class="line"> <span class="keyword">synchronized</span> (mPauseLock) {</span><br><span class="line"> <span class="keyword">if</span> (mPauseFlag) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> mPauseLock.wait();</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// <span class="doctag">TODO</span> Something</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>PS:<a href="http://blog.sina.com.cn/s/blog_4c0706560101175j.html" target="_blank" rel="external">原文链接</a>在此。</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="引言">引言</h2><p>Android的车辆监控端应用开发中,碰到了轨迹回放控制,播放、暂停、停止不知道该怎么实现的问题,虽然知道要用线程。然后找到了这篇文章,讲解的非常清晰,在此记录之,文末有原文链接。最后将整个轨迹回放实现完成后,再看代码恍然大悟,这不就是音乐播放器的播放、暂停、停止的实现,当初看播放器源码应该很容易就解决了,结果重复造了轮子,不过还是很有收获的。</p>
<p>PS:程序示例部分是我在原作者的基础上修改而来,感觉应该更通俗易懂些,希望能对需要的人有帮助,有什么错误的话还请指正,谢谢。</p>]]>
</summary>
<category term="线程" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B/"/>
<category term="线程操作" scheme="http://jellybins.github.io/tags/%E7%BA%BF%E7%A8%8B%E6%93%8D%E4%BD%9C/"/>
<category term="Android" scheme="http://jellybins.github.io/categories/Android/"/>
</entry>
<entry>
<title><![CDATA[Java参数传递-值传递or引用传递?]]></title>
<link href="http://jellybins.github.io/2013/09/25/Java%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92-%E5%80%BC%E4%BC%A0%E9%80%92or%E5%BC%95%E7%94%A8%E4%BC%A0%E9%80%92%EF%BC%9F/"/>
<id>http://jellybins.github.io/2013/09/25/Java参数传递-值传递or引用传递?/</id>
<published>2013-09-25T13:26:01.000Z</published>
<updated>2015-08-21T13:29:19.955Z</updated>
<content type="html"><![CDATA[<h2 id="简单类型是按值传递的">简单类型是按值传递的</h2><p>Java 方法的参数是简单类型的时候,是按值传递的 (pass by value)。这一点我们可以通过一个简单的例子来说明:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 例 1 * Test.java */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(<span class="keyword">boolean</span> test)</span> </span>{</span><br><span class="line"> test = !test;</span><br><span class="line"> System.out.println(<span class="string">"In test(boolean) : test = "</span> + test);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">boolean</span> test = <span class="keyword">true</span>;</span><br><span class="line"> System.out.println(<span class="string">"Before test(boolean) : test = "</span> + test);</span><br><span class="line"> test(test);</span><br><span class="line"> System.out.println(<span class="string">"After test(boolean) : test = "</span> + test);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>Before test(boolean) : test = true<br>In test(boolean) : test = false<br>After test(boolean) : test = true</p>
</blockquote>
<p>不难看出,虽然在 <code>test(boolean)</code> 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对 <code>main(String[])</code> 方法里的 test 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。</p>
<a id="more"></a>
<h2 id="什么是引用">什么是引用</h2><p>Java 是传值还是传引用,问题主要出在对象的传递上,因为 Java 中简单类型没有引用。既然争论中提到了引用这个东西,为了搞清楚这个问题,我们必须要知道引用是什么。<br> <br>简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似C语言中指针的东西,它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。<br> <br>如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间来保存。但是它们的值是相同的,都指示同一个对象在内存的中位置。比如<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">String a = <span class="string">"Hello"</span>;</span><br><span class="line">String b = a;</span><br></pre></td></tr></table></figure></p>
<p>这里 a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象 “Hello”。也许你还觉得不够直观,因为 String 对象的值本身是不可更改的 (像 b = “World”; b = a; 这种情况不是改变了 “World” 这一对象的值,而是改变了它的引用 b 的值使之指向了另一个 String 对象 a)。那么我们用 StringBuffer 来举一个例子:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 例 2 * Test.java */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> StringBuffer a = <span class="keyword">new</span> StringBuffer(<span class="string">"Hello"</span>);</span><br><span class="line"> StringBuffer b = a;</span><br><span class="line"> b.append(<span class="string">", World"</span>);</span><br><span class="line"> System.out.println(<span class="string">"a is "</span> + a);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>a is Hello, World</p>
</blockquote>
<p>这个例子中 a 和 b 都是引用,当改变了 b 指示的对象的值的时候,从输出结果来看,a 所指示的对象的值也改变了。所以 a 和 b 都指向同一个对象即包含 “Hello” 的一个 StringBuffer 对象。</p>
<p>这里我描述了两个要点:引用是一种数据类型,保存了对象在内存中的地址,这种类型即不是我们平时所说的简单数据类型也不是类实例(对象);不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。
</p>
<h2 id="对象是如何传递的呢">对象是如何传递的呢</h2><p>关于对象的传递,有两种说法,即“它是按值传递的”和“它是按引用传递的”。这两种说法各有各的道理,但是它们都没有从本质上去分析,即致于产生了争论。既然现在我们已经知道了引用是什么东西,那么现在不妨来分析一下对象作是参数是如何传递的。还是先以一个程序为例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 例 3 * Test.java */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(StringBuffer str)</span> </span>{</span><br><span class="line"> str.append(<span class="string">", World!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> StringBuffer string = <span class="keyword">new</span> StringBuffer(<span class="string">"Hello"</span>);</span><br><span class="line"> test(string);</span><br><span class="line"> System.out.println(string);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>运行结果:</p>
<blockquote>
<p>Hello, World!</p>
</blockquote>
<p>test(string) 调用了 test(StringBuffer) 方法,并将 string 作为参数传递了进去。这里 string 是一个引用,这一点是勿庸置疑的。前面提到,引用是一种数据类型,而且不是对象,所以它不可能按引用传递,所以它是按值传递的,它么它的值究竟是什么呢? 是对象的地址。</p>
<p>由此可见,对象作为参数的时候是按值传递的,对吗?错!为什么错,让我们看另一个例子:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 例 4 * Test.java */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(String str)</span> </span>{</span><br><span class="line"> str = <span class="string">"World"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String string = <span class="string">"Hello"</span>;</span><br><span class="line"> test(string);</span><br><span class="line"> System.out.println(string);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>运行结果:</p>
<blockquote>
<p>Hello</p>
</blockquote>
<p>为什么会这样呢?因为参数 str是一个引用,而且它与string是不同的引用,虽然它们都是同一个对象的引用。str = “World” 则改变了 str 的值,使之指向了另一个对象,然而 str 指向的对象改变了,但它并没有对 “Hello” 造成任何影响,而且由于string和str是不同的引用,str的改变也没有对string造成任何影响,结果就如例中所示。</p>
<p>其结果是推翻了参数按值传递的说法。那么,对象作为参数的时候是按引用传递的了?也错!因为上一个例子的确能够说明它是按值传递的。结果,就像光到底是波还是粒子的问题一样,Java方法的参数是按什么传递的问题,其答案就只能是:即是按值传递也是按引用传递,只是参照物不同,结果也就不同。</p>
<h2 id="正确看待传值还是传引用的问题">正确看待传值还是传引用的问题</h2><p>要正确的看待这个问题必须要搞清楚为什么会有这样一个问题。</p>
<p>实际上,问题来源于 C,而不是 Java。C 语言中有一种数据类型叫做指针,于是将一个数据作为参数传递给某个函数的时候,就有两种方式:传值,或是传指针,它们的区别,可以用一个简单的例子说明:<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 例 5 * test.c */</span> </span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SwapValue</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{ </span><br><span class="line"> <span class="keyword">int</span> t = a; a = b; b = t;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SwapPointer</span><span class="params">(<span class="keyword">int</span> * a, <span class="keyword">int</span> * b)</span> </span>{ </span><br><span class="line"> <span class="keyword">int</span> t = * a; * a = * b; * b = t; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">()</span> </span>{ </span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">0</span>, b = <span class="number">1</span>; </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"1 : a = %d, b = %d\n"</span>, a, b);</span><br><span class="line"> SwapValue(a, b); </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"2 : a = %d, b = %d\n"</span>, a, b);</span><br><span class="line"> SwapPointer(&a, &b);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"3 : a = %d, b = %d\n"</span>, a, b);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>运行结果:</p>
<blockquote>
<p>1 : a = 0, b = 1<br>2 : a = 0, b = 1<br>3 : a = 1, b = 0</p>
</blockquote>
<p>大家可以明显的看到,按指针传递参数可以方便的修改通过参数传递进来的值,而按值传递就不行。</p>
<p>当 Java 成长起来的时候,许多的 C 程序员开始转向学习 Java,他们发现,使用类似 SwapValue 的方法仍然不能改变通过参数传递进来的简单数据类型的值,但是如果是一个对象,则可能将其成员随意更改。于是他们觉得这很像是 C 语言中传值/传指针的问题。但是 Java 中没有指针,那么这个问题就演变成了传值/传引用的问题。可惜将这个问题放在 Java 中进行讨论并不恰当。<br> <br>讨论这样一个问题的最终目的只是为了搞清楚何种情况才能在方法函数中方便的更改参数的值并使之长期有效。</p>
<p>Java 中,改变参数的值有两种情况,第一种,使用赋值号“=”直接进行赋值使其改变,如例 1 和例 4;第二种,对于某些对象的引用,通过一定途径对其成员数据进行改变,如例 3。对于第一种情况,其改变不会影响到方法该方法以外的数据,或者直接说源数据。而第二种方法,则相反,会影响到源数据——因为引用指示的对象没有变,对 其成员数据进行改变则实质上是改变的该对象。
</p>
<h2 id="如何实现类似_swap_的方法">如何实现类似 swap 的方法</h2><p> <br>传值还是传引用的问题,到此已经算是解决了,但是我们仍然不能解决这样一个问题:如果我有两个 int 型的变量 a 和 b,我想写一个方法来交换它们的值,应该怎么办?</p>
<p>结论很让人失望——没有办法!因此,我们只能具体情况具体讨论,以经常使用交换方法的排序为例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 例 6 * Test.java */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(<span class="keyword">int</span>[] data, <span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> t = data[a];</span><br><span class="line"> data[a] = data[b];</span><br><span class="line"> data[b] = t;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span>[] data = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">10</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> data[i] = (<span class="keyword">int</span>) (Math.random() * <span class="number">100</span>);</span><br><span class="line"> System.out.print(<span class="string">" "</span> + data[i]);</span><br><span class="line"> }</span><br><span class="line"> System.out.println();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">9</span>; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = i; j < <span class="number">10</span>; j++) {</span><br><span class="line"> <span class="keyword">if</span> (data[i] > data[j]) {</span><br><span class="line"> swap(data, i, j);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> System.out.print(<span class="string">" "</span> + data[i]);</span><br><span class="line"> }</span><br><span class="line"> System.out.println();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>运行结果(情况之一):</p>
<blockquote>
<p>78 69 94 38 95 31 50 97 84 1<br>1 31 38 50 69 78 84 94 95 97</p>
</blockquote>
<p><code>swap(int[] data, int a, int b)</code> 方法在内部实际上是改变了 data 所指示的对象的成员数据,即上述讨论的第二种改变参数值的方法。希望大家能够举一反三,使用类似的方法来解决相关问题。</p>
<h2 id="总结">总结</h2><p>一般人都会认同Java中只有值传递,是在讲如果传递的是对象的引用,其实传到参数中的也只是这个引用的一个副本,而不是真正的引用本身,并且把引用看是一种数据类型的话,传递的其实只是引用这种数据的一个副本值。而大多人数人常讲的基本数据类型是值传递,复合数据类型是引用传递,其实更严谨的说法本人感觉应该是“基本数据类型是值传递,复合数据类型是按引用传递”~传递的确实是引用,但是却不是引用的本身,而只是引用的一个副本,这两个引用都指向同一个对象,任何一个修改对象都会引起另一个跟着改变,但是如果更改副本引用的指向,却不会改变原引用的指向,这就是等号赋值和通过方法改变引用所指向的对象的区别(有点玩文字游戏的感觉)。</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="简单类型是按值传递的">简单类型是按值传递的</h2><p>Java 方法的参数是简单类型的时候,是按值传递的 (pass by value)。这一点我们可以通过一个简单的例子来说明:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 例 1 * Test.java */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(<span class="keyword">boolean</span> test)</span> </span>{</span><br><span class="line"> test = !test;</span><br><span class="line"> System.out.println(<span class="string">"In test(boolean) : test = "</span> + test);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">boolean</span> test = <span class="keyword">true</span>;</span><br><span class="line"> System.out.println(<span class="string">"Before test(boolean) : test = "</span> + test);</span><br><span class="line"> test(test);</span><br><span class="line"> System.out.println(<span class="string">"After test(boolean) : test = "</span> + test);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果:</p>
<blockquote>
<p>Before test(boolean) : test = true<br>In test(boolean) : test = false<br>After test(boolean) : test = true</p>
</blockquote>
<p>不难看出,虽然在 <code>test(boolean)</code> 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对 <code>main(String[])</code> 方法里的 test 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。</p>]]>
</summary>
<category term="值传递" scheme="http://jellybins.github.io/tags/%E5%80%BC%E4%BC%A0%E9%80%92/"/>
<category term="参数传递" scheme="http://jellybins.github.io/tags/%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92/"/>
<category term="引用传递" scheme="http://jellybins.github.io/tags/%E5%BC%95%E7%94%A8%E4%BC%A0%E9%80%92/"/>
<category term="Java" scheme="http://jellybins.github.io/categories/Java/"/>
</entry>
</feed>