<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>-Flyぁ梦- &#187; Life in Coding</title>
	<atom:link href="http://blog.11034.org/category/coding/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.11034.org</link>
	<description></description>
	<lastBuildDate>Sun, 22 Jun 2025 08:59:05 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>64位Linux下Java进程堆外内存迷之64M问题</title>
		<link>http://blog.11034.org/2016-09/64bits_linux_arena_memory.html</link>
		<comments>http://blog.11034.org/2016-09/64bits_linux_arena_memory.html#comments</comments>
		<pubDate>Fri, 09 Sep 2016 10:44:42 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[内存泄露]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2870</guid>
		<description><![CDATA[起因，监控系统检查到link机器内存耗尽，用top查看，果然有一个java进程占用了近24g内存（包括VIRT [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>起因，监控系统检查到link机器内存耗尽，用top查看，果然有一个java进程占用了近24g内存（包括VIRT和RES），但是启动java进程的参数是java -server -Xmx12g -Xms12g -XX:PermSize=50m，理论上只应该占用12g再多一些，所以问题就在这个进程了。此进程主要处理Socket IO读写，使用的是Java NIO。</p>
<p><span id="more-2870"></span></p>
<p><a href="http://stackoverflow.com/questions/561245/virtual-memory-usage-from-java-under-linux-too-much-memory-used" target="_blank">http://stackoverflow.com/questions/561245/virtual-memory-usage-from-java-under-linux-too-much-memory-used</a> 这里详细描述了top命令里内存显示的意义。</p>
<p>用jstat -gcutil $pid看，发现堆使用率却很低，甚至都没有怎么出现过Full GC，Young GC也很少，堆内存完全够用。<code class="markdown_inline_code">jmap -histo:live  $pid命令（注：此命令调用会先调用Full GC一次）</code>也看不出有任何对象占用内存很大的情况。</p>
<p>到这里据同事提醒，怀疑是堆外内存泄露。一般情况下是<code class="markdown_inline_code">ByteBuffer.allocateDirect()</code>这个API可以开辟堆外内存空间，但是搜索源代码并没有发现有这个调用。</p>
<p>然后据水寒同学提示，可以通过<code class="markdown_inline_code">cat /proc/$pid/maps</code>（也可以通过<code class="markdown_inline_code">pmap $pid</code>命令）来查看内存使用。然后就发现了问题：</p>
<ul>
<li>一大块内存正好是12g，应该就是通过-Xmx设置的内存，无标识符</li>
<li>有一块3.5g的内存，最后一列标识符是[heap]</li>
<li>有111块正好为64M或者非常接近64M的内存块，无标识符</li>
</ul>
<p>显然问题出在这111 * 64m = 6g这里，搜索源代码并没有发现有常量64的使用，一般代码也不会硬coding这么大一块内存呀。如果问题出在引用的某个第三方jar包就难定位问题了。</p>
<p>因为觉得这个64M内存块具有非常显著的特点，于是就通过Google和百度，找到如下：</p>
<pre class="markdown_pre"><code><a href="http://blog.2baxb.me/archives/918" target="_blank">http://blog.2baxb.me/archives/918</a>

<a href="https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage" target="_blank">https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage</a>

<a href="http://it.taocms.org/01/6808.htm" target="_blank">http://it.taocms.org/01/6808.htm</a>

<a href="https://infobright.com/blog/malloc_arena_max/" target="_blank">https://infobright.com/blog/malloc_arena_max/</a>

这几篇文章，尤其是ibm那篇文章讲的最细致，Linux glibc &gt;= 2.10 (RHEL 6) malloc may show excessive virtual memory usage。

大致意思是，这个版本的glibc控制的malloc函数，会在64位的Linux的中，给每一个CPU核开辟8个64M内存的缓冲区，用于提高性能。

然后这个问题也被Hadoop官方发现并指出，然后推荐控制环境变量MALLOC_ARENA_MAX=4来达到最优设置。

修改方法：启动java进程脚本前面加入，export MALLOC_ARENA_MAX=4</code></pre>
<h4  class="related_post_title">看看 linux , 内存 , 内存泄露</h4><ul class="related_post"><li>2015-08-28 -- <a target="_blank" href="http://blog.11034.org/2015-08/tomcat_linux.html" title="Linux下搭建Tomcat环境">Linux下搭建Tomcat环境</a></li><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2013-05-15 -- <a target="_blank" href="http://blog.11034.org/2013-05/fan_error.html" title="小黑风扇坏两天，被逼看完一本书">小黑风扇坏两天，被逼看完一本书</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-09/64bits_linux_arena_memory.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>jabber和XMPP简述原理</title>
		<link>http://blog.11034.org/2016-09/jabber_and_xmpp.html</link>
		<comments>http://blog.11034.org/2016-09/jabber_and_xmpp.html#comments</comments>
		<pubDate>Fri, 09 Sep 2016 10:43:45 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Life in Coding]]></category>
		<category><![CDATA[im]]></category>
		<category><![CDATA[xmpp]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2868</guid>
		<description><![CDATA[为了实现不同IM（即时通讯软件，比如QQ、GTalk）之间可以互相通信，jabber和XMPP为此而生。jab [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>为了实现不同IM（即时通讯软件，比如QQ、GTalk）之间可以互相通信，jabber和XMPP为此而生。jabber是Linux中的一个程序，设计了一套开源的协议，用于连接不通的IM，然后XMPP以jabber协议为基础并扩展之。每一个IM程序服务端与客户端之间肯定有一套保密并加密的协议，这个是不可能容易让外部获知的，不然IM软件聊天内容就泄露了，谁还敢用。XMPP是让不同的IM之间互相通信，跟IM程序本身毫无关系。</p>
<p><span id="more-2868"></span></p>
<p>简单的说就是，QQ在自身服务器外，再搭建一个jabber服务器，GTalk也同理，分别称之为jabberQ和jabberG。QQ用户的标识以@qq.com即为，GTalk用户的标识以@gmail.com结尾。首先QQ用户想要与GTalk用户通信，QQ服务器发现这个请求要发给外部用户而非内部QQ用户，将请求转到jabberQ服务器，jabberQ服务器识别到此外部用户以@gmail.com结尾，需要知道jabberG服务器的IP地址是多少，通过DNS协议去查询gmail.com，其中的SRV Records有一项<code class="markdown_inline_code">_xmpp-server._tcp.domain</code>可以获知到对应的jabber服务器域名。</p>
<p>可以通过以下命令手动查询：</p>
<pre class="markdown_pre"><code>dig SRV _xmpp-server._tcp.gmail.com

;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 42765
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 4

;; QUESTION SECTION:
;_xmpp-server._tcp.gmail.com. IN SRV

;; ANSWER SECTION:
_xmpp-server._tcp.gmail.com. 900 IN SRV 20 0 5269 alt1.xmpp-server.l.google.com.
_xmpp-server._tcp.gmail.com. 900 IN SRV 20 0 5269 alt2.xmpp-server.l.google.com.
_xmpp-server._tcp.gmail.com. 900 IN SRV 20 0 5269 alt3.xmpp-server.l.google.com.
_xmpp-server._tcp.gmail.com. 900 IN SRV 20 0 5269 alt4.xmpp-server.l.google.com.
_xmpp-server._tcp.gmail.com. 900 IN SRV 5 0 5269 xmpp-server.l.google.com.

具体详见：<a href="http://wiki.xmpp.org/web/SRV_Records#XMPP_SRV_records" target="_blank">http://wiki.xmpp.org/web/SRV_Records#XMPP_SRV_records</a></code></pre>
<p>然后jabberQ服务器，就通过域名alt1.xmpp-server.l.google.com获取到IP地址去与jabberG服务器建立Socket连接了，当然通过XMPP协议规定的xml内容交互，所以jabberQ服务器会将QQ服务器内部的请求转化为开源的XMPP请求。经过jabberG服务器确认后，连接建立，之后的XMPP协议就可以欢快的走起来了，包括加好友、好友上线/下线状态通知、发消息等。jabberG服务器获取到来自jabberQ的XMPP通知后，将此XMPP请求转化为GTalk内部的数据请求转发给GTalk服务器，然后推送到GTalk客户端。</p>
<p>简单地说就这样子，QQ客户端到GTalk客户端的一次数据请求就完成了。</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2012-09-01 -- <a target="_blank" href="http://blog.11034.org/2012-09/batman3.html" title="蝙蝠侠3：黑暗骑士崛起 IMAX">蝙蝠侠3：黑暗骑士崛起 IMAX</a></li><li>2011-08-27 -- <a target="_blank" href="http://blog.11034.org/2011-08/nicolas_cage_movies.html" title="尼古拉斯·凯奇系列电影">尼古拉斯·凯奇系列电影</a></li><li>2013-04-19 -- <a target="_blank" href="http://blog.11034.org/2013-04/system_reinstall.html" title="再也不说会重装系统了>_<">再也不说会重装系统了>_<</a></li><li>2017-10-29 -- <a target="_blank" href="http://blog.11034.org/2017-10/guangzhou_macau.html" title="2017国庆超级假期广州澳门游">2017国庆超级假期广州澳门游</a></li><li>2016-06-12 -- <a target="_blank" href="http://blog.11034.org/2016-06/saint_seiya_soul_of_gold.html" title="圣斗士星矢-黄金魂">圣斗士星矢-黄金魂</a></li></ul><h4 class="related_post_title">看看 Life in Coding </h4><ul class="related_post"><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-09-10 -- <a target="_blank" href="http://blog.11034.org/2014-09/dotnet_csharp_excel.html" title="记C#和Excel开发">记C#和Excel开发</a></li><li>2011-06-11 -- <a target="_blank" href="http://blog.11034.org/2011-06/google_doodle_guitar.html" title="2011-6-9 Google Doodle之电子琴">2011-6-9 Google Doodle之电子琴</a></li><li>2011-03-10 -- <a target="_blank" href="http://blog.11034.org/2011-03/learning_cs.html" title="学计算机的尼玛伤不起啊！！！！！！">学计算机的尼玛伤不起啊！！！！！！</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-09/jabber_and_xmpp.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Linux MySQL查询默认情况下不区分大小写</title>
		<link>http://blog.11034.org/2016-08/mysql_linux_ignore_case.html</link>
		<comments>http://blog.11034.org/2016-08/mysql_linux_ignore_case.html#comments</comments>
		<pubDate>Tue, 30 Aug 2016 12:50:53 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[大小写]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2879</guid>
		<description><![CDATA[如题，在like搜索中不区分大小写是OK的，但是用=匹配也不区分感觉就有点怪了，具体看项目看需求。此次碰到从数 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>如题，在like搜索中不区分大小写是OK的，但是用=匹配也不区分感觉就有点怪了，具体看项目看需求。此次碰到从数据库中读数据读到了大小写数据，但是代码中却是区分大小写又拦截掉了，导致数据的不一致情况出现。</p>
<p><span id="more-2879"></span></p>
<pre class="markdown_pre"><code>这样子的SQL写法可以区分大小写

mysql&gt; select * from t1 where name = binary 'YOU';</code></pre>
<p>如果不想这么麻烦而想服务一开启就让大小写一致的话，需要修改配置文件my.ini或者my.cnf<br />
[mysqld]<br />
lower_case_table_names=1（0：区分；1：不区分）</p>
<p>mysql&gt; show variables like &#8216;%case_table%';<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+<br />
| Variable_name          | Value |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+<br />
| lower_case_table_names | 1     |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2011-10-20 -- <a target="_blank" href="http://blog.11034.org/2011-10/zoj_100_ac.html" title="ZOJ起步 100AC">ZOJ起步 100AC</a></li><li>2012-01-10 -- <a target="_blank" href="http://blog.11034.org/2012-01/nikon_s3000_repair.html" title="尼康S3000 镜头维修">尼康S3000 镜头维修</a></li><li>2013-06-17 -- <a target="_blank" href="http://blog.11034.org/2013-06/huihang_trail.html" title="徽杭古道">徽杭古道</a></li><li>2017-05-03 -- <a target="_blank" href="http://blog.11034.org/2017-05/guilin.html" title="五一桂林阳朔游">五一桂林阳朔游</a></li><li>2012-05-15 -- <a target="_blank" href="http://blog.11034.org/2012-05/router_in_play_framework.html" title="Play框架的Router机制和jregex包">Play框架的Router机制和jregex包</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-08/mysql_linux_ignore_case.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>读java concurrency in practice</title>
		<link>http://blog.11034.org/2016-08/java_concurrency_in_practice.html</link>
		<comments>http://blog.11034.org/2016-08/java_concurrency_in_practice.html#comments</comments>
		<pubDate>Thu, 18 Aug 2016 07:15:05 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Life in reading]]></category>
		<category><![CDATA[同步]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2875</guid>
		<description><![CDATA[这是一本好书（Java源码作者Doug Lea是作者之一），关于Java并发的各个方面，介绍的很深入，应该算是 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>这是一本好书（Java源码作者Doug Lea是作者之一），关于Java并发的各个方面，介绍的很深入，应该算是进阶一类，很多地方不好理解。一开始打算阅读英文版，倒不是英文看不懂，不过还是有很多内容的确理解起来很费劲，尤其是老外写书的风格（废话特别多 = = 一页密密麻麻的英文，让人找不到关键点在哪儿），看几页就没兴趣再看下去哎&#8230;后有幸找到了中文翻译版，而且翻译的很有水准，拜谢翻译工作者韩锴和方妙。</p>
<p>找不到翻译版的同学，可以私信我提供。</p>
<p><span id="more-2875"></span></p>
<p>可以说，自己对Java真正的并发同步的理解还处于入门阶段，看了一遍书很多地方估计还是没有get到的，不过已经学到不少了。这本书值得以后再细细研读。</p>
<ul>
<li>可重入的概念、可重入锁和java.util.concurrent.locks.ReentrantLock。</li>
</ul>
<p>详见：<strong><a class="row-title" title="编辑“可重入锁、ReentrantLock、AQS、Condition”" href="http://blog.11034.org/wp-admin/post.php?post=2833&amp;action=edit" target="_blank">可重入锁、ReentrantLock、AQS、Condition</a></strong></p>
<ul>
<li>基于AQS的各种同步工具类</li>
</ul>
<p>有ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore、FutureTask、CyclicBarrier等</p>
<p>详见：<strong><a class="row-title" title="编辑“ReentrantReadWriteLock简单分析”" href="http://blog.11034.org/wp-admin/post.php?post=2838&amp;action=edit">ReentrantReadWriteLock简单分析</a>  <a class="row-title" title="编辑“CountDownLatch简单分析”" href="http://blog.11034.org/wp-admin/post.php?post=2845&amp;action=edit">CountDownLatch简单分析</a> <a class="row-title" title="编辑“Semaphore简单分析”" href="http://blog.11034.org/wp-admin/post.php?post=2850&amp;action=edit">Semaphore简单分析</a> <a class="row-title" title="编辑“FutureTask简单分析和用法”" href="http://blog.11034.org/wp-admin/post.php?post=2853&amp;action=edit">FutureTask简单分析和用法</a></strong></p>
<ul>
<li>reordering重排序、happened-before关系、volatile真正语义</li>
<li>基于硬件的原子CAS算法</li>
<li>内置synchronized锁、基于CAS算法的锁、CAS原子类、volatile修饰的性能相关</li>
<li>org.apache.http.annotation包下有一些有用的Annotation，比如ThreadSafe、NotThreadSafe等</li>
</ul>
<h4  class="related_post_title">看看 同步 , 并发 , 锁</h4><ul class="related_post"><li>2016-06-16 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantreadwritelock.html" title="ReentrantReadWriteLock简单分析">ReentrantReadWriteLock简单分析</a></li><li>2016-06-13 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantlock.html" title="可重入锁、ReentrantLock、AQS、Condition">可重入锁、ReentrantLock、AQS、Condition</a></li><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li></ul><h4 class="related_post_title">看看 Java , Life in reading </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-08/java_concurrency_in_practice.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>线程清理</title>
		<link>http://blog.11034.org/2016-08/thread_stop.html</link>
		<comments>http://blog.11034.org/2016-08/thread_stop.html#comments</comments>
		<pubDate>Fri, 05 Aug 2016 09:26:53 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[线程]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2862</guid>
		<description><![CDATA[执行脚本遇到一个情况，脚本无法自动退出完毕，必须手动执行Ctrl-C才能退出，之前遇到过这个问题是因为数据库连 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>执行脚本遇到一个情况，脚本无法自动退出完毕，必须手动执行Ctrl-C才能退出，之前遇到过这个问题是因为数据库连接没有被正确关闭导致，这次的原因是一个另起的线程没有正常停止的原因。</p>
<p><span id="more-2862"></span></p>
<p>此线程A维护一个队列，线程A负责从队列中消费，主线程负责往线程中添加，队列池用的LinkedBlockQueue。线程A主要工作就是，从queue中take()出一个资源，但是处理相关逻辑，然后循环。</p>
<p>所以在脚本执行完毕后，需要给线程一个通知，当队列为空时，自动退出循环结束线程。然后在主线程中等待theadA.join()即可。</p>
<p>然后的问题是，LinkedBlockQueue.take()是无限等待的，所以还需要执行theadA.interrupt()才能够跳出这个无限等待的API。</p>
<p>但是如果执行theadA.interrupt()时，队列还不为空，就会出问题。</p>
<p>所以使用LinkedBlockQueue.poll(long timeout, TimeUnit unit)，就不需要执行theadA.interrupt()，也能够判断到队列为空自动退出循环了。</p>
<p>但是有超时时间的poll方法性能上应该要比无超时时间的take()要低，而且正常执行过程中当队列为空时反复超时跳出等待再进去新的等待，浪费CPU。</p>
<p>这是个有点纠结的问题。</p>
<p>以下代码，用stop()来立刻停止线程（无论queue是否还有剩下的未处理），用interrupt()来等待处理完queue中所有后再退出。</p>
<p>这里或者可以在interrupt()中返回queue.size()，根据返回的size若为0可以执行theadA.interrupt()，不为0则不执行，但是又有问题同步队列的size()方法会引起线程同步的问题（size()方法并没有太大的意义）。</p>
<p><code class="markdown_inline_code">running和interrupt变量是会在不同的线程write和read的，所以必须要用volatile修饰，保证一致性</code></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">volatile</span> <span style="color: #000066; font-weight: bold;">boolean</span> running <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> stop<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
	running <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">false</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">volatile</span> <span style="color: #000066; font-weight: bold;">boolean</span> interrupt <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">false</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> interrupt<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
	interrupt <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
@Override
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> run<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">while</span><span style="color: #009900;">&#40;</span>running<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #003399;">Object</span> obj <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
				obj <span style="color: #339933;">=</span> queue.<span style="color: #006633;">take</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				obj <span style="color: #339933;">=</span> queue.<span style="color: #006633;">poll</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">5</span>, TimeUnit.<span style="color: #006633;">SECONDS</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">InterruptedException</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				logger.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;used to break queue.take() infinite waiting&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">Exception</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				logger.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;get obj erorr&quot;</span>, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>obj <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
				<span style="color: #000000; font-weight: bold;">do</span><span style="color: #009900;">&#40;</span>obj<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>interrupt<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
				<span style="color: #000066; font-weight: bold;">int</span> size <span style="color: #339933;">=</span> queue.<span style="color: #006633;">size</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>size <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
					logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;interrupt after queue size == 0&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
					<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
				<span style="color: #009900;">&#125;</span>
				<span style="color: #000000; font-weight: bold;">else</span><span style="color: #009900;">&#123;</span>
					logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;interrupt but queue size == &quot;</span> <span style="color: #339933;">+</span> size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #009900;">&#125;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">Exception</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			logger.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;erorr&quot;</span>, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;stop succ&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2013-01-08 -- <a target="_blank" href="http://blog.11034.org/2013-01/jiuzhai_2.html" title="跨年游之九寨沟二进沟篇">跨年游之九寨沟二进沟篇</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li><li>2012-01-10 -- <a target="_blank" href="http://blog.11034.org/2012-01/nikon_s3000_repair.html" title="尼康S3000 镜头维修">尼康S3000 镜头维修</a></li><li>2017-10-29 -- <a target="_blank" href="http://blog.11034.org/2017-10/macau.html" title="澳门走走">澳门走走</a></li><li>2010-07-17 -- <a target="_blank" href="http://blog.11034.org/2010-07/firstday_hongkong.html" title="唐大威项目香港游，@香港的第一天">唐大威项目香港游，@香港的第一天</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-08/thread_stop.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>FutureTask简单分析和用法</title>
		<link>http://blog.11034.org/2016-06/futuretask.html</link>
		<comments>http://blog.11034.org/2016-06/futuretask.html#comments</comments>
		<pubDate>Tue, 21 Jun 2016 07:54:40 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2853</guid>
		<description><![CDATA[Callable、Future和FutureTask，结合Executors和ExecutorService， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Callable、Future和FutureTask，结合Executors和ExecutorService，将某些特定的计算任务交给其他线程（由统一的线程池管理），让主线程进行阻塞和等待执行结果，用在RPC服务比较直观，或者这个计算结果被多个线程所需等情况。</p>
<p>从整体上看，使用FutureTask降低了程序整体的效率，肯定不如在原线程中直接执行的快，而且因为主线程需要阻塞等待计算结果，所以并没有增加并发度。所以FutureTask在项目中一般也比较少见。<br />
<span id="more-2853"></span></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> FutureTaskExample <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">class</span> Task <span style="color: #000000; font-weight: bold;">implements</span> Callable<span style="color: #339933;">&lt;</span>Integer<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#123;</span>
        @Override
        <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Integer</span> call<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// 第一种方式, 隐藏了FutureTask</span>
        ExecutorService executor <span style="color: #339933;">=</span> Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        Task task <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Task<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        Future<span style="color: #339933;">&lt;</span>Integer<span style="color: #339933;">&gt;</span> future <span style="color: #339933;">=</span> executor.<span style="color: #006633;">submit</span><span style="color: #009900;">&#40;</span>task<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003399;">Integer</span> ret <span style="color: #339933;">=</span> future.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// 第二种方式, 直接使用FutureTask</span>
        ExecutorService executor <span style="color: #339933;">=</span> Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        Task task <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Task<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        FutureTask<span style="color: #339933;">&lt;</span>Integer<span style="color: #339933;">&gt;</span> futureTask <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> FutureTask<span style="color: #339933;">&lt;</span>Integer<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span>task<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        executor.<span style="color: #006633;">submit</span><span style="color: #009900;">&#40;</span>futureTask<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003399;">Integer</span> ret <span style="color: #339933;">=</span> futureTask.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// 第三种方式, 直接使用Thread</span>
        Task task <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Task<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        FutureTask<span style="color: #339933;">&lt;</span>Integer<span style="color: #339933;">&gt;</span> futureTask <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> FutureTask<span style="color: #339933;">&lt;</span>Integer<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span>task<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003399;">Thread</span> thread <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Thread</span><span style="color: #009900;">&#40;</span>futureTask<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// FutureTask实现了Runnable接口</span>
        thread.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        futureTask.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        executor.<span style="color: #006633;">shutdown</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<h2>FutureTask的一个实用场景</h2>
<p>当一个计算任务非常重量级，非常耗资源和耗时间（比如网络请求或者数据库请求），而且多个线程都需要等待这个计算任务的结果，这个时候FutureTask的优势就体现出来了。只要线程同步的去控制FutureTask的run()，然后所有线程都只要等待FutureTask.get()获取结果就可以了，这样子不会每个线程并发性的去多次重复执行计算任务。</p>
<p>很显然的一个场景就是 数据库查询+缓存。比如查询某个table的id=xxx的对象，查询结果在内存缓存起来，在缓存为空的情况下并发第一次的访问可能引起多次SQL请求，使用FutureTask就可以避免这个问题。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MemCache<span style="color: #339933;">&lt;</span>K, V<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> ConcurrentHashMap<span style="color: #339933;">&lt;</span>K, Future<span style="color: #339933;">&lt;</span>V<span style="color: #339933;">&gt;&gt;</span> cache<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">private</span> V getResultFromDB<span style="color: #009900;">&#40;</span>K k<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000000; font-weight: bold;">return</span> v<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> V get<span style="color: #009900;">&#40;</span>K k<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">while</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
            Future<span style="color: #339933;">&lt;</span>V<span style="color: #339933;">&gt;</span> f <span style="color: #339933;">=</span> cache.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>k<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>f <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
                Callable<span style="color: #339933;">&lt;</span>V<span style="color: #339933;">&gt;</span> eval <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Callable<span style="color: #339933;">&lt;</span>V<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
                    <span style="color: #000000; font-weight: bold;">public</span> V call<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throw</span> <span style="color: #003399;">InterruptedException</span><span style="color: #009900;">&#123;</span>
                        <span style="color: #000000; font-weight: bold;">return</span> getResultFromDB<span style="color: #009900;">&#40;</span>k<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                    <span style="color: #009900;">&#125;</span>
                <span style="color: #009900;">&#125;</span>
                FutureTask<span style="color: #339933;">&lt;</span>V<span style="color: #339933;">&gt;</span> ft <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> FutureTask<span style="color: #339933;">&lt;</span>V<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span>eval<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #666666; font-style: italic;">// 这一行避免可能的执行并发产生的多个不同的futureTask</span>
                f <span style="color: #339933;">=</span> cache.<span style="color: #006633;">putIfAbsent</span><span style="color: #009900;">&#40;</span>arg, ft<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>f <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
                    f <span style="color: #339933;">=</span> ft<span style="color: #339933;">;</span>
                    <span style="color: #666666; font-style: italic;">// 保证执行的一定是第一个futureTask</span>
                    ft.<span style="color: #006633;">run</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #000000; font-weight: bold;">try</span><span style="color: #009900;">&#123;</span>
                <span style="color: #000000; font-weight: bold;">return</span> f.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span>CancellationException e<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
                <span style="color: #666666; font-style: italic;">// 避免缓存污染（一个计算被取消或者异常，未来获取不到值），则要从缓存中移除</span>
                cache.<span style="color: #006633;">remove</span><span style="color: #009900;">&#40;</span>k, f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span>ExecutionException e<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
                <span style="color: #000000; font-weight: bold;">throw</span> e<span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<h2>FutureTask源码分析</h2>
<p>也是类似一把共享锁，所有get操作都是试图进行acquireShared加锁，当然在run执行完毕之前，会一直加锁失败并阻塞；run中执行callable.call()结束，设置计算结果值V后，进行releaseShared解锁后，所有get操作被唤醒并拿到结果值V。如果执行中途执行cancel，也会进行releaseShared解锁，所有get操作会拿到null结果值。</p>
<p>state值用来保存task的执行状态值，0是初始态，1表示running，2表示ran done，4表示cancel。</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2010-11-28 -- <a target="_blank" href="http://blog.11034.org/2010-11/%e8%b4%a7%e5%b8%81%e6%88%98%e4%ba%89.html" title="货币战争">货币战争</a></li><li>2010-06-07 -- <a target="_blank" href="http://blog.11034.org/2010-06/shanghai_hongkong.html" title="上海、香港，我要来啦~~">上海、香港，我要来啦~~</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2011-02-20 -- <a target="_blank" href="http://blog.11034.org/2011-02/fire_disaster.html" title="家对面的住宅楼居然失火了，惊心动魄！">家对面的住宅楼居然失火了，惊心动魄！</a></li><li>2013-11-28 -- <a target="_blank" href="http://blog.11034.org/2013-11/kunming.html" title="彩云之南-昆明，青旅的偶遇">彩云之南-昆明，青旅的偶遇</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/futuretask.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Semaphore简单分析</title>
		<link>http://blog.11034.org/2016-06/semaphore.html</link>
		<comments>http://blog.11034.org/2016-06/semaphore.html#comments</comments>
		<pubDate>Tue, 21 Jun 2016 06:36:11 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2850</guid>
		<description><![CDATA[java.util.concurrent.Semaphore AQS实现的一个变种，用来预先设置一个阈值（可进 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>java.util.concurrent.Semaphore</p>
<p>AQS实现的一个变种，用来预先设置一个阈值（可进入量），然后当做类似共享锁的方式地进行acquire和release，<code class="markdown_inline_code">但是每次acquire不记录当前acquire的线程对象（这是Semaphore和Lock最大的区别）</code>，即线程A操作的acquire可以由线程B来release。也有公平和非公平两种方式。</p>
<p><span id="more-2850"></span></p>
<p>一般用在管理一个对象池，设置初始容量N，每次getObject就acquire，每次returnObject就release，因为没有对对象池本身采用任何同步加锁代码，即使线程A的acquire在阻塞中，线程B的release依然可以进行。</p>
<p>Semaphore的最大容量也可以动态增加，可以通过release和release(int n)持续扩大容量（即使没有任何线程进行acquire）（虽然API名称上不直观）。</p>
<p>AQS的代码解析见：<a class="title" href="/2016-06/reentrantlock.html" target="_blank" rel="bookmark">可重入锁、ReentrantLock、AQS、Condition</a></p>
<p>reducePermits和nonfairTryAcquireShared的区别在于，reducePermits不管减少容量后的state值是多少（可以为负值）阻塞等待CAS完成，nonfairTryAcquireShared则需要判断减少容量后的state值是正值则阻塞等待CAS完成，负值则返回acquire失败回到AQS中加入Sync队列。</p>
<p>&nbsp;</p>
<h4  class="related_post_title">看看 锁</h4><ul class="related_post"><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li><li>2016-06-16 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantreadwritelock.html" title="ReentrantReadWriteLock简单分析">ReentrantReadWriteLock简单分析</a></li><li>2016-06-13 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantlock.html" title="可重入锁、ReentrantLock、AQS、Condition">可重入锁、ReentrantLock、AQS、Condition</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/semaphore.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CountDownLatch简单分析</title>
		<link>http://blog.11034.org/2016-06/countdownlatch.html</link>
		<comments>http://blog.11034.org/2016-06/countdownlatch.html#comments</comments>
		<pubDate>Fri, 17 Jun 2016 10:18:30 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[共享锁]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2845</guid>
		<description><![CDATA[也是利用AQS实现的一个变种，用来预先设置一个阈值（任务数）后主线程调用await()持续阻塞，然后由其他线程 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>也是利用AQS实现的一个变种，用来预先设置一个阈值（任务数）后主线程调用await()持续阻塞，然后由其他线程执行任务，执行完后调用countDown()将阈值减1，当所有任务执行完毕，阈值减为0，主线程await()被唤醒跳出阻塞，继续执行。<span id="more-2845"></span></p>
<h2>使用例子</h2>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> CountDownLatchDemo <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">InterruptedException</span> <span style="color: #009900;">&#123;</span>
    	CountDownLatch latch <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CountDownLatch<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 两个任务线程</span>
    	Worker worker1 <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Worker<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;A&quot;</span>, latch<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    	Worker worker2 <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Worker<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;B&quot;</span>, latch<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    	worker1.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//</span>
    	worker2.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//</span>
&nbsp;
    	latch.<span style="color: #006633;">await</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 阻塞等待所有Worker线程结束</span>
        <span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;all work done&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">class</span> Worker <span style="color: #000000; font-weight: bold;">extends</span> <span style="color: #003399;">Thread</span> <span style="color: #009900;">&#123;</span>
    	<span style="color: #000000; font-weight: bold;">private</span> CountDownLatch latch<span style="color: #339933;">;</span>
    	<span style="color: #000000; font-weight: bold;">public</span> Worker<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> workerName, CountDownLatch latch<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    	    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">latch</span> <span style="color: #339933;">=</span> latch<span style="color: #339933;">;</span>
    	<span style="color: #009900;">&#125;</span>
    	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> run<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
	    <span style="color: #000000; font-weight: bold;">try</span><span style="color: #009900;">&#123;</span>
	        doWork<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	    <span style="color: #009900;">&#125;</span>
            <span style="color: #666666; font-style: italic;">// 必须放在finally中保证能够释放（不然出异常主线程无法恢复）</span>
	    <span style="color: #000000; font-weight: bold;">finally</span><span style="color: #009900;">&#123;</span>
	        latch.<span style="color: #006633;">countDown</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//线程执行结束, 计数器减1</span>
	    <span style="color: #009900;">&#125;</span>
    	<span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span> 
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<h2>源码分析</h2>
<p>针对AQS的分析见：<a href="http://blog.11034.org/2016-06/reentrantlock.html" target="_blank">可重入锁、ReentrantLock、AQS、Condition</a> 和 <a href="http://blog.11034.org/2016-06/reentrantreadwritelock.html" target="_blank">ReentrantReadWriteLock简单分析</a>。</p>
<p>CountDownLatch的内置Sync类相当简单</p>
<ul>
<li>构造函数，初始化count值，写入AQS的state（相当于已经有count的对象持有锁）</li>
<li>主线程调用await()方法，就是执行AQS.acquireSharedInterruptibly，而Sync.tryAcquireShared，只有当state == 0才能够acquire成功（即只有当count减为0，await才会跳出阻塞）</li>
<li>其他线程调用countDown()方法，就是执行AQS.releaseShared(1)，而Sync.tryReleaseShared，最简单的无限循环阻塞保证release成功</li>
<li>多于count数量的执行countDown()方法，不会再更新state值，state减为0就不变了</li>
<li>多次await也没有意义，在state==0后，await操作就是共享加锁，都可以瞬间成功</li>
</ul>
<h4  class="related_post_title">看看 共享锁 , 锁</h4><ul class="related_post"><li>2016-06-16 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantreadwritelock.html" title="ReentrantReadWriteLock简单分析">ReentrantReadWriteLock简单分析</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-13 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantlock.html" title="可重入锁、ReentrantLock、AQS、Condition">可重入锁、ReentrantLock、AQS、Condition</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/countdownlatch.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ReentrantReadWriteLock简单分析</title>
		<link>http://blog.11034.org/2016-06/reentrantreadwritelock.html</link>
		<comments>http://blog.11034.org/2016-06/reentrantreadwritelock.html#comments</comments>
		<pubDate>Thu, 16 Jun 2016 11:49:10 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[共享锁]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2838</guid>
		<description><![CDATA[AQS的另一个实现，可重入的读写锁，其中读锁是共享锁，写锁是排他锁。用法和ReentrantLock基本一致。 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>AQS的另一个实现，可重入的读写锁，其中读锁是共享锁，写锁是排他锁。用法和ReentrantLock基本一致。<span id="more-2838"></span></p>
<p>ReentrantLock的源码分析见：<a href="http://blog.11034.org/2016-06/reentrantlock.html" target="_blank">可重入锁、ReentrantLock、AQS、Condition</a>，作为本文的铺垫。</p>
<p>ReentrantLock利用AQS的state值作为重入次数记录，所以理论上可以重入int的最大值；ReentrantReadWriteLock同样利用AQS的state值来作为重入次数记录，只不过32位的int值，前16位用来记录读锁的重入次数，后16位用来记录写锁的重入次数，所以读锁和写锁的可重入次数最多都只有65535次。</p>
<p>ReentrantReadWriteLock的代码结构和ReentrantLock基本一致，先有一个Sync类继承自AQS实现了全部的写锁和读锁的加锁、解锁代码，然后再有UnfairSync和FairSync继承自Sync来实现非公平和公平策略，两个子类中只实现了readerShouldBlock()和writerShouldBlock()。不同的是，还有两个新的类，ReadLock和WriteLock，内部持有Sync实例，ReadLock的lock()和unlock()调用acquireShared()和releaseShared()，WriteLock的lock()和unlock()调用acquire()和release()，都只是对Sync实例方法的直接调用，没啥可看的东西。</p>
<h2>Sync类</h2>
<h3>state值拆分</h3>
<p>一开始的SHARED_SHIFT、SHARED_UNIT、MAX_COUNT、EXCLUSIVE_MASK变量就是对state值进行前16位和后16的拆分，还有后面sharedCount()和exclusiveCount()方法。</p>
<h3>针对每个线程的读锁的HoldCounter</h3>
<p>state值的前16位，记录了所有线程的读锁的重入次数，所以每个线程中还要自己单独在记录一下读锁重入次数（才能做到读锁的重入和释放）。这个数据就保存在<code class="markdown_inline_code">HoldCounter</code>类中，并用<code class="markdown_inline_code">ThreadLocalHoldCounter extends ThreadLocal&lt;HoldCounter&gt;</code>类来存储。</p>
<p>HoldCounter cachedHoldCounter这个变量可以无视，缓存上一次进入读锁的HoldCounter对象，没有这个功能也没太大影响每次都从ThreadLocal中取就可以了，只是提高了效率。</p>
<h2>Sync类的子类，UnfairSync和FairSync</h2>
<p>都只实现了writerShouldBlock()和readerShouldBlock()方法。</p>
<p>FairSync简单，所有试图acquire锁都必须是Sync队列中的first才行，保证公平性。</p>
<p>UnfairSync的write操作，没有任何需要Block的场景，而read需要判断Sync队列的first是否是写（独占）请求，表明了写的优先级比较高，读在很多情况下要给写让步，防止写锁饥饿。</p>
<h2>代码实现</h2>
<h3>写锁的实现</h3>
<p>调用AQS.acquire和AQS.release实现，然后通过Sync.tryAcquire()和Sync.tryRelease()实现，和ReentrantLock基本没有差别。在tryAcquire中既要判断state的互斥部分（是否有写锁占用），也要判断state的共享部分（是否有读锁占用）。</p>
<h3>读锁的加锁实现</h3>
<p>调用AQS.acquireShared和AQS.releaseShared实现，然后通过Sync.tryAcquireShared()和Sync.tryReleaseShared()实现。</p>
<h4>Sync.tryAcquireShared（<span style="color: #ff0000;">此方法阻塞至acquire成功或者返回失败</span>）</h4>
<ol>
<li>判断是否被写锁占用且当前线程不是写锁拥有者，return -1</li>
<li>判断读锁重进入总数（所有线程）是否超过最大值</li>
<li>根据公平性原则（Sync子类的readerShouldBlock()方法），判断是否可以加读锁</li>
<li>然后就尝试CAS加锁，若成功则获取当前线程的HoldCounter对象，更新count++（更新缓存hc对象），return 1</li>
<li>CAS miss，call fullTryAcquireShared()</li>
<li>fullTryAcquireShared 循环开始（<span style="color: #ff0000;">此方法阻塞</span>）</li>
<li>判断是否被写锁占用且当前线程不是写锁拥有者，return -1</li>
<li>再判断(rh.count | w) == 0 &amp;&amp; readerShouldBlock(current)，(rh.count | w) == 0就是rh.count == 0 || w == 0（当前线程未占有读锁或未占用写锁，即除了占有写锁后的读锁重进入的情况），都要根据公平性原则（Sync子类的readerShouldBlock()方法），判断是否可以加读锁</li>
<li>判断读锁重入是否超过最大值</li>
<li>尝试CAS加锁，若成功更新rh.count++，return 1</li>
<li>CAS miss，回到循环开始，步骤6</li>
</ol>
<p>官方注释中说，fullTryAcquireShared方法handles CAS misses and reentrant reads not dealt with in tryAcquireShared，其中CAS miss是很好理解的，但是reentrant reads还真没看出来（读重入完全有可能在tryAcquireShared的CAS里就完成了呀，可能作者的意思是读重入因为并发比较高所以容易CAS miss？）。</p>
<h4>AQS.acquireShared</h4>
<p>在执行完Sync.tryAcquireShared失败时，会call AQS.doAcquireShared，将线程添加到AQS的Sync队列中，并循环等待加锁成功，类似独占锁执行的AQS.acquireQueued方法。</p>
<h4>AQS.doAcquireShared</h4>
<ol>
<li>添加节点到AQS的Sync队列中</li>
<li>无限循环开始</li>
<li>判断当前节点若是first节点，到步骤4，若不是到步骤6</li>
<li>call tryAcquireShared，若加锁成功到步骤5，若失败到步骤6</li>
<li>call setHeadAndPropagate，更新Sync队列的head节点，并在一定条件下调用AQS.doReleaseShared，并跳出循环返回</li>
<li>判断当前节点线程是否需要park，并执行线程park</li>
<li>回到循环开始，步骤2</li>
</ol>
<p>doAcquireShared和acquireQueued的区别在于，加锁成功后，acquireQueued仅仅就是更新了head节点，而doAcquireShared不仅更新了head节点，还会在一定条件下call doReleaseShared方法去激活下一个等待的节点。</p>
<h4>AQS.setHeadAndPropagate和AQS.doReleaseShared</h4>
<p>这一点可以理解为，因为doAcquireShared是获取共享锁，获取共享锁的前提是没有独占锁被持有，而共享锁被一方获取后，其他方可以同时去尝试获取共享锁，所以可以来激活下一个等待的是共享模式的节点线程，提高效率。这样子可以把Sync队列中依次顺序的共享锁请求全部打开并满足，提高了并发。</p>
<p>所以这里也可以看出，当一个线程获得写锁后，并不是所有等待写锁的线程都会被激活并获得共享写锁，因为Sync队列中是按照公平原则来的，只要队列中有一个独占锁请求（写锁），后面的共享锁（读锁）请求是依然被阻塞的，按照时间顺序保证了这些读锁请求会在写锁之后执行，读到新的数据。</p>
<h3>读锁的解锁实现</h3>
<p>解锁的简单了，调用Sync.tryReleaseShared()，解锁成功后调用AQS.doReleaseShared()，激活下面节点的线程。</p>
<h4>Sync.tryReleaseShared（<span style="color: #ff0000;">此方法阻塞至release成功，返回是否锁被释放</span>）</h4>
<h3>读锁与写锁的实现区别</h3>
<p>读锁（共享锁）的加锁的阻塞，在Sync.fullTryAcquireShared中也有实现（在Sync中出现acquire失败后才进入AQS里的阻塞实现），而在Sync中实现acquire成功，是不会调用AQS.doReleaseShared去对下面节点进行线程唤醒的（这个就有点不明白了）。</p>
<p>ps：读锁（共享锁）的加锁这一块是最复杂的，理解也还不甚到位。</p>
<p>读锁（共享锁）的解锁也需要在阻塞，也在Sync中实现，因为共享锁在解锁时也存在并发；而写锁（独占锁）是不需要阻塞实现的，因为解锁的时刻肯定是只有当前线程可以操作锁对象，不存在并发。</p>
<h3>tryReadLock和tryWriteLock</h3>
<p>这两个方法acquire的逻辑和普通的lock没有区别，除了少判断了writerShouldBlock()和readerShouldBlock()方法，即失去了公平性保证也抛弃了写优先于读的策略。</p>
<h4  class="related_post_title">看看 共享锁 , 并发 , 锁</h4><ul class="related_post"><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li><li>2016-06-13 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantlock.html" title="可重入锁、ReentrantLock、AQS、Condition">可重入锁、ReentrantLock、AQS、Condition</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/reentrantreadwritelock.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>可重入锁、ReentrantLock、AQS、Condition</title>
		<link>http://blog.11034.org/2016-06/reentrantlock.html</link>
		<comments>http://blog.11034.org/2016-06/reentrantlock.html#comments</comments>
		<pubDate>Mon, 13 Jun 2016 13:51:14 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2833</guid>
		<description><![CDATA[本文简单介绍可重入锁的概念，简单介绍java中内置的synchronized锁和java.util.concu [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文简单介绍可重入锁的概念，简单介绍java中内置的<code class="markdown_inline_code">synchronized锁</code>和<code class="markdown_inline_code">java.util.concurrent.locks.ReentrantLock</code>，然后大致介绍下ReentrantLock的代码实现。ReentrantLock和AQS代码逻辑真心复杂和难懂，尤其是AQS中Node的ws值中的SIGNAL值的用法和线程park/unpark的用法，持续几周看了好几天终于算是给弄明白了！<span id="more-2833"></span></p>
<h2>可重入锁</h2>
<p>可重入锁，这是一个理论范畴的概念，具体可自己查询。意思就是，在当前线程已经获取锁的情况下，可以再度获取锁而不会引起死锁。最好理解的就是java代码中最常见的synchronized锁，它就是一个可重入锁，因为同为synchronized修饰的方法A和B，在A中call B，是没有问题的，从A到B的过程就是锁的重入。</p>
<p>所以很好理解，可重入锁需要在内部维持一个进入次数N和占有锁的线程对象S，当N == 0时表示锁闲置，获取锁时set N = 1并set S = currentThread，每次进入锁N++，每次退出锁N&#8211;，直到N == 0锁被释放并set S = null，当然前提所有操作要保证currentThread == S才行。</p>
<h2>synchronized锁和ReentrantLock</h2>
<p>两者都是可重入锁</p>
<p>区别在于：</p>
<table>
<tbody>
<tr>
<td>synchronized</td>
<td>ReentrantLock</td>
</tr>
<tr>
<td>语法内置，自动加锁和释放锁得到保证</td>
<td>手动lock和unlock，unlock需在finally中，否则临界区代码异常会引起死锁</td>
</tr>
<tr>
<td>非公平锁</td>
<td>提供默认的非公平锁和公平锁实现</td>
</tr>
<tr>
<td></td>
<td>加锁API丰富，lock、tryLock、lockInterruptibly、tryLock(timeout)</td>
</tr>
<tr>
<td>利用object或者class作为锁本身来wait和notify</td>
<td>提供Condition机制来await和signal，更灵活和好理解</td>
</tr>
<tr>
<td>使用难度低，效率偏低</td>
<td>使用难度较高，效率更高</td>
</tr>
</tbody>
</table>
<p>虽然效率上Reentrantlock偏高，但如果不熟悉可能会引起更多问题，在大多数情况下理应优先使用synchronized，只有在需要利用ReentrantLock的公平机制、其他lock API的情况下，或者对于效率有着非常严格要求的情况下，才推荐使用ReentrantLock。</p>
<h2>ReentrantLock与AbstractQueuedSynchronizer</h2>
<p>ReentrantLock是AbstractQueuedSynchronizer（AQS）的一个实现（不是子类），绝大部分的实现都在于AQS之内，AQS仅仅暴露了几个API供子类实现，而ReentrantLock仅仅实现了其中两个：</p>
<ul>
<li>protected boolean tryAcquire(int arg)：尝试加锁</li>
<li>protected boolean tryRelease(int arg)：尝试解锁</li>
</ul>
<h3>AQS内部数据结构</h3>
<ul>
<li>静态内部类Node，作为一个双向list的节点，存储有线程对象和一个状态值，以及另外一个变量表明是否为独占</li>
<li>Node head, tail 节点，维护Node的双向list，此为等待加锁的线程节点Sync列表</li>
<li>int state值，供子类使用，可表示不同意义；在Reentrantlock中表示重入锁次数</li>
<li>内部类ConditionObject，每个ConditionObject内部维护一个Node队列，是为等待此condition的线程队列</li>
</ul>
<h3>AQS中Node的waitStatus（以下简称ws值）状态值</h3>
<ul>
<li>0，正常状态，不停地尝试去获取锁</li>
<li>1，CANCELLED，表示此节点已被Cancel，退出竞争锁</li>
<li>-1，SIGNAL，表示当前node在release时后面的节点需要被唤醒</li>
<li>-2，CONDITION，表示当前node在某一个ConditionObject的等待队列中，不在Sync队列中</li>
<li>-3，PROPAGATE</li>
</ul>
<h3>AQS中Node的nextWaiter节点</h3>
<p>nextWaiter == Node.EXCLUSIVE == null，表明为独占</p>
<p>nextWaiter == Node.SHARED== new Node()，表明为共享</p>
<h2>ReentrantLock中的Sync、NonfairSync、FairSync</h2>
<p>Reentrantlock中的Sync才是AQS的直接子类，nonfairTryAcquire()方法实现了非公平锁的实现，并实现了tryRelease()方法，基本就是通过CAS原理，通过AQS的state值和ownerThread来实现，很好理解。</p>
<p>NonfairSync又是Sync的子类，tryAcquire()直接调用nonfairTryAcquire()。</p>
<p>FairSync也是是Sync的子类，单独实现tryAcquire()，比nonfairTryAcquire()多了一个isFirst(current)的判断，就保证了公平性。后面会具体讲。</p>
<h2>AQS中对线程的park和unpark</h2>
<p>利用unsafe实现的，对线程挂起和唤醒的操作。</p>
<p>有2个地方会引起线程park</p>
<ol>
<li>ConditionObject.await方法，使得当前线程节点进入condition队列</li>
<li>AQS.acquireQueued的循环中，当前线程节点的prev节点的ws值为SIGNAL</li>
</ol>
<p>有2个地方会引起线程unpark</p>
<ol>
<li>ConditionObject的doSignal方法，使得线程节点从condition队列移除，添加到Sync队列，若prev节点被Cancel了或者设置prev.ws为SIGNAL失败，则立即激活本节点线程</li>
<li>当前线程节点的prev节点线程抢占锁并release，且prev.ws == SIGNAL，唤醒next节点</li>
</ol>
<h2>ReentrantLock的代码实现</h2>
<p>此部分内容，不考虑cancelAcquire操作、其他lock操作和线程中断等复杂情况，以最基本的逻辑讲述。</p>
<h3>非公平锁的lock</h3>
<ol>
<li>新的线程，在NonfairSync.lock中直接尝试CAS加锁，成功则返回</li>
<li>若失败，call AQS.acquire(1)，1表示进入次数加1</li>
<li>AQS.acquire：!tryAcquire(n) &amp;&amp; acquireQueued(addWaiter(Node.EXCLUSIVE), n)</li>
<li>调用子类的tryAcquire再试一遍加锁：即Sync.nonfairTryAcquire()，通过CAS尝试加锁或者计算重进入，成功则返回</li>
<li>若失败，回到AQS.acquire，执行acquireQueued(addWaiter(Node.EXCLUSIVE), n)</li>
<li>AQS.addWaiter，新建Node节点（独占），通过AQS.enq方法无限循环保证（阻塞式地）添加到Sync队列中</li>
<li>AQS.acquireQueued，无限循环开始：判断节点是否为Sync队列中的first有效节点并call tryAcquire尝试加锁，若成功，移除原head，设置新head = first，返回</li>
<li>循环中：若失败，call AQS.shouldParkAfterFailedAcquire判断node是否需要被park，然后park当前线程</li>
<li>循环结束，回到步骤7</li>
</ol>
<ul>
<li>第1、4步中可以看到，新的线程尝试加锁可以无视Sync队列中等待的其他线程直接尝试加锁（而且代码更简单效率更高，如下图中的黄色快速通道所示），此为非公平性原理</li>
</ul>
<div id="attachment_2840" style="width: 403px" class="wp-caption aligncenter"><a href="http://blog.11034.org/wp-content/uploads/2016/06/sync_queue.png"><img class="size-full wp-image-2840" src="http://blog.11034.org/wp-content/uploads/2016/06/sync_queue.png" alt="AQS中的同步队列与加锁通道" width="393" height="126" /></a><p class="wp-caption-text">AQS中的同步队列与加锁通道</p></div>
<ul>
<li>第7步中得知，在Sync队列中等待的线程，是严格按照先后顺序获取锁的。所以是非严格的非公平性。</li>
<li>第8步中shouldParkAfterFailedAcquire的原理，若node.prev的ws为SIGNAL，返回true; 否则，尝试set node.prev.ws = SIGNAL并返回false（在下一次的循环中，就可能会返回true了）</li>
</ul>
<h3>公平锁的lock</h3>
<p>和非公平锁的区别就在于</p>
<ol>
<li>没有第1步，直接进入AQS.acquire(1)</li>
<li>FairSync.tryAcquire中，需要额外判断Sync队列为空或当前线程是Sync队列中的first才尝试CAS加锁，成功则返回</li>
<li>从第5步开始完全一致</li>
</ol>
<p>取消非公平锁的第1步，调整第4步，就保证了严格按照Sync队列来获得锁，即为公平性。</p>
<h3>unlock</h3>
<p>公平锁和非公平锁的unlock操作是一样的，通过调用AQS.release(1)实现</p>
<ol>
<li>call Sync.tryRelease，就是通过CAS将state &#8211; 1，若最后state &gt; 0，锁还未释放，返回</li>
<li>若最后state == 0（锁被释放），若head节点（上一个加锁成功的节点）的ws值 != 0（就是为SIGNAL的情况下）， call AQS.unparkSuccessor(head)</li>
<li>AQS.unparkSuccessor(node)：先CAS设置node.ws = 0</li>
<li>在node节点后找到第一个没有被cancel（ws值 &lt;= 0）的节点，尝试将其unpark（这里要unpark的next线程节点，不一定真的被park了，就是保证next节点的线程可以work来抢锁）</li>
</ol>
<h2>AQS中Condition的实现</h2>
<p>condition被新建时，就在内部维护了一个队列</p>
<h3>condition.await</h3>
<ol>
<li>addConditionWaiter()，将当前线程新建为Node，设置ws值为CONDITION，添加到condition的队列中</li>
<li>call AQS.fullyRelease，将当前线程获得的锁完全释放（即无论重入几次，state之间减为0）</li>
<li>无限循环开始：当node.ws == CONDITION或node不在Sync队列中，park线程</li>
<li>等待condition被其他线程signal或signalAll了，节点ws值被修改且被移回Sync队列中（循环条件被打破），但此时线程仍在park中</li>
<li>在Sync队列中，node.prev得到锁并释放后，node节点线程被unpark</li>
<li>跳出循环，call acquireQueued()，进入正常锁竞争逻辑</li>
</ol>
<h3>condition.signal和condition.signalAll</h3>
<p>将condition的队列中的first节点或者全部节点，调用condition.doSignal()，然后就是调用AQS.transferForSignal()</p>
<ol>
<li>首先将本节点的ws从CONDITION设为0</li>
<li>call AQS.enq，将节点加入到Sync队列中</li>
<li>判断node.prev.ws值为CANCELLED，或者设置node.prev.ws = SIGNAL失败，直接将本节点线程unpark</li>
</ol>
<p>经过这3步，就可以打破await中的循环条件，然后当node.prev得到锁并释放后，node节点线程被unpark</p>
<h4  class="related_post_title">看看 并发 , 锁</h4><ul class="related_post"><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-06-16 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantreadwritelock.html" title="ReentrantReadWriteLock简单分析">ReentrantReadWriteLock简单分析</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li><li>2016-06-17 -- <a target="_blank" href="http://blog.11034.org/2016-06/countdownlatch.html" title="CountDownLatch简单分析">CountDownLatch简单分析</a></li><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/reentrantlock.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Tomcat监听shutdown释放数据库连接池</title>
		<link>http://blog.11034.org/2016-06/tomcat_shutdown.html</link>
		<comments>http://blog.11034.org/2016-06/tomcat_shutdown.html#comments</comments>
		<pubDate>Wed, 08 Jun 2016 12:41:47 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[数据库]]></category>
		<category><![CDATA[进程]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2836</guid>
		<description><![CDATA[开发时因为更新代码，频繁重启Tomcat，遇到一个问题：在执行shutdown脚本后，Tomcat进程没有关闭 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>开发时因为更新代码，频繁重启Tomcat，遇到一个问题：在执行shutdown脚本后，Tomcat进程没有关闭依然存在（但是HTTP服务已经停止），需要手动执行kill命令才行。查了一些资料结合经验，应该是所使用的数据库连接池中的连接没有被释放的问题引起的。所以解决的办法，就是想办法做一个Tomcat的shutdown的事件监听，然后手动释放数据库连接即可。<span id="more-2836"></span></p>
<h2>Tomcat的shutdown的事件监听</h2>
<p>很简单，实现一个ServletContextListener的实例，并注册到web.xml中即可。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> ShutdownListener <span style="color: #000000; font-weight: bold;">implements</span> ServletContextListener  <span style="color: #009900;">&#123;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> Logger logger <span style="color: #339933;">=</span> Logger.<span style="color: #006633;">getLogger</span><span style="color: #009900;">&#40;</span>ShutdownListener.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	@Override
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> contextDestroyed<span style="color: #009900;">&#40;</span>ServletContextEvent arg0<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;contextDestroyed&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;DBPool shutdown start...&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
			DBPool.<span style="color: #006633;">clear</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			DBPool.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">Throwable</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			logger.<span style="color: #006633;">fatal</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;&quot;</span>, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;DBPool shutdown done&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	@Override
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> contextInitialized<span style="color: #009900;">&#40;</span>ServletContextEvent arg0<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;contextInitialized&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>


<div class="wp_syntax"><table><tr><td class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>xxx.xxx.xxx.ShutdownListener<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener-class<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></td></tr></table></div>

<h2>数据库连接池的关闭和释放</h2>
<p>代码中没有使用复杂的数据库连接池，是用<code class="markdown_inline_code">org.apache.commons.pool.impl.GenericObjectPool</code>和<code class="markdown_inline_code">org.apache.commons.pool.BasePoolableObjectFactory</code>组合实现的。</p>
<p>在ServletContextListener的contextDestroyed方法中</p>
<ol>
<li>执行GenericObjectPool.clear()，是将池内缓存的对象全部销毁</li>
<li>执行GenericObjectPool.close()，关闭对象池，不再支持borrowObject()，但依然支持returnObject()方法且return后将对象立即销毁</li>
</ol>
<p>做完这两步，执行Tomcat的shutdown脚本后，Tomcat进程过几秒就会自动关闭了。</p>
<h4  class="related_post_title">看看 Tomcat , 数据库 , 进程</h4><ul class="related_post"><li>2016-06-06 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_https.html" title="Tomcat启用https服务">Tomcat启用https服务</a></li><li>2015-08-28 -- <a target="_blank" href="http://blog.11034.org/2015-08/tomcat_linux.html" title="Linux下搭建Tomcat环境">Linux下搭建Tomcat环境</a></li><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/tomcat_shutdown.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Tomcat启用https服务</title>
		<link>http://blog.11034.org/2016-06/tomcat_https.html</link>
		<comments>http://blog.11034.org/2016-06/tomcat_https.html#comments</comments>
		<pubDate>Mon, 06 Jun 2016 14:27:44 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[安全]]></category>
		<category><![CDATA[证书]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2816</guid>
		<description><![CDATA[第一步，获取证书 这个就去百度吧，既有免费证书，也有收费的证书，当然收费的证书会更靠谱更受大众认同（比如Geo [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><span id="more-2816"></span></p>
<h2>第一步，获取证书</h2>
<p>这个就去百度吧，既有免费证书，也有收费的证书，当然收费的证书会更靠谱更受大众认同（比如GeoTrust的）。</p>
<p>然后证书分为3个等级，DV（Domain Validation，证明当前站点在指定域名下，最常见的），OV（Organization Validation，证明当前站点在指定公司or组织下，浏览器会在URL前显示公司名称的），EV（Extended Validation，比OV更加复杂安全的证书，一般为网银所使用）。</p>
<p>这里选择国内的免费证书：https://www.wosign.com/</p>
<p>在它家网站上注册用户，申请免费证书，要填写指定的域名地址和证书保护密码，然后就可以下载到证书了。下载的zip包里有专门供tomcat使用的jks证书。</p>
<h2>第二步，部署https</h2>
<p>将<code class="markdown_inline_code">apache-tomcat/conf/server.xml</code>中原来被注释的&lt;Connector port=&#8221;8443&#8243; &#8230; /&gt;打开，如下配置：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;Connector</span> <span style="color: #000066;">port</span>=<span style="color: #ff0000;">&quot;443&quot;</span> <span style="color: #000066;">protocol</span>=<span style="color: #ff0000;">&quot;org.apache.coyote.http11.Http11NioProtocol&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">acceptCount</span>=<span style="color: #ff0000;">&quot;100&quot;</span> <span style="color: #000066;">executor</span>=<span style="color: #ff0000;">&quot;tomcatThreadPool&quot;</span> <span style="color: #000066;">URIEncoding</span>=<span style="color: #ff0000;">&quot;UTF-8&quot;</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">compression</span>=<span style="color: #ff0000;">&quot;on&quot;</span> <span style="color: #000066;">compressableMimeType</span>=<span style="color: #ff0000;">&quot;text/html,text/css,application/javascript&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">SSLEnabled</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">scheme</span>=<span style="color: #ff0000;">&quot;https&quot;</span> <span style="color: #000066;">secure</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">clientAuth</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000066;">sslProtocol</span>=<span style="color: #ff0000;">&quot;TLS&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">keystoreFile</span>=<span style="color: #ff0000;">&quot;/home/.../xxx.com.jks&quot;</span> <span style="color: #000066;">keystorePass</span>=<span style="color: #ff0000;">&quot;xxxxxxx&quot;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></td></tr></table></div>

<p>这里的配置要和<code class="markdown_inline_code">&lt;Connector port="80"  .../&gt;</code>的配置要完全一致，第一行是基础协议，第二行是优化参数和设置编码，第三行是开启静态资源压缩，最后两行是对于SSL特殊的设置。</p>
<pre class="markdown_pre"><code>在https的Connector中忘记加上URIEncoding="UTF-8"，会导致https中文异常而http正常
（http的&lt;Connector /&gt;自带这个属性）</code></pre>
<h2>第三步，http自动跳转到https</h2>
<p>给<code class="markdown_inline_code">&lt;Connector port="80"  .../&gt;</code>加上属性<code class="markdown_inline_code">redirectPort="443"</code>，这个默认就有。</p>
<p>然后再修改<code class="markdown_inline_code">apache-tomcat/conf/web.xml</code>，最后加上一段：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="xml" style="font-family:monospace;">    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;security-constraint<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;web-resource-collection<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;web-resource-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Protected Context<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/web-resource-name<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;url-pattern<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>/*<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/url-pattern<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/web-resource-collection<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #808080; font-style: italic;">&lt;!-- auth-constraint goes here if you requre authentication --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;user-data-constraint<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;transport-guarantee<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>CONFIDENTIAL<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/transport-guarantee<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/user-data-constraint<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/security-constraint<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></td></tr></table></div>

<p>搞定</p>
<h2>第三步，https的注意事项</h2>
<ul>
<li>在https下，如果页面内还存在有http链接的内容（图片、js等静态资源），则会产生警告，并在浏览器URL栏中失去绿色锁的标志</li>
<li>在https下，如果页面内企图发起http的post请求，则会默认被浏览器禁止，并产生警告</li>
</ul>
<h4  class="related_post_title">看看 HTTPS , Tomcat , 安全 , 证书</h4><ul class="related_post"><li>2016-06-08 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_shutdown.html" title="Tomcat监听shutdown释放数据库连接池">Tomcat监听shutdown释放数据库连接池</a></li><li>2015-08-28 -- <a target="_blank" href="http://blog.11034.org/2015-08/tomcat_linux.html" title="Linux下搭建Tomcat环境">Linux下搭建Tomcat环境</a></li><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-06/tomcat_https.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Tomcat开启gzip压缩</title>
		<link>http://blog.11034.org/2016-05/tomcat_gzip.html</link>
		<comments>http://blog.11034.org/2016-05/tomcat_gzip.html#comments</comments>
		<pubDate>Thu, 26 May 2016 03:10:47 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2810</guid>
		<description><![CDATA[&#60;Connector port=&#34;8080&#34; protocol=&#34;org. [&#8230;]]]></description>
				<content:encoded><![CDATA[
<div class="wp_syntax"><table><tr><td class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;Connector</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">port</span>=<span style="color: #ff0000;">&quot;8080&quot;</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">protocol</span>=<span style="color: #ff0000;">&quot;org.apache.coyote.http11.Http11NioProtocol&quot;</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">acceptorThreadCount</span>=<span style="color: #ff0000;">&quot;8&quot;</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">executor</span>=<span style="color: #ff0000;">&quot;tomcatThreadPool&quot;</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">URIEncoding</span>=<span style="color: #ff0000;">&quot;UTF-8&quot;</span> </span>
<span style="color: #009900;">    <span style="color: #000066;">compression</span>=<span style="color: #ff0000;">&quot;on&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">compressableMimeType</span>=<span style="color: #ff0000;">&quot;text/html,text/css,application/javascript,text/xml,text/plain&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">connectionTimeout</span>=<span style="color: #ff0000;">&quot;20000&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">redirectPort</span>=<span style="color: #ff0000;">&quot;8443&quot;</span> </span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></td></tr></table></div>

<p>主要是compression和compressableMimeType两行。</p>
<p>Tomcat7中，必须是application/javascript，而不能使text/javascript。</p>
<p>注意如果开启了https服务，在<Connector port="443" .../>中也别忘了设置开启compression和compressableMimeType两行。</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2012-05-13 -- <a target="_blank" href="http://blog.11034.org/2012-05/java_play_framework.html" title="Java Play framework">Java Play framework</a></li><li>2010-09-15 -- <a target="_blank" href="http://blog.11034.org/2010-09/one_humorous_fiction.html" title="一篇风趣的小说">一篇风趣的小说</a></li><li>2010-12-16 -- <a target="_blank" href="http://blog.11034.org/2010-12/wp-sns-share_update_to_1-4.html" title="wp-sns-share更新至1.4">wp-sns-share更新至1.4</a></li><li>2011-04-28 -- <a target="_blank" href="http://blog.11034.org/2011-04/currency_war_2.html" title="货币战争：金权天下">货币战争：金权天下</a></li><li>2010-08-17 -- <a target="_blank" href="http://blog.11034.org/2010-08/three_kingdom_love.html" title="三国情">三国情</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-05/tomcat_gzip.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spring-MVC中的一些问题</title>
		<link>http://blog.11034.org/2016-03/spring_mvc_applicationcontext.html</link>
		<comments>http://blog.11034.org/2016-03/spring_mvc_applicationcontext.html#comments</comments>
		<pubDate>Tue, 22 Mar 2016 13:52:10 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2805</guid>
		<description><![CDATA[想法：Spring-mvc（Spring v3.2）中，仅对注册过（通过@RequestMapping注解）的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>想法：Spring-mvc（Spring v3.2）中，仅对注册过（通过<code class="markdown_inline_code">@RequestMapping</code>注解）的url进入到controller层，其他非法url在Filter里就拦截掉，统一处理节省资源。因为在filter的doChain之前，给本次请求从对象池中获取一个操作db的对象，对无效url也分配的话就浪费了资源。</p>
<p>实现：通过Spring-mvc的<code class="markdown_inline_code">RequestMappingHandlerMapping</code>来获取注册过的url path，再根据HttpServletRequest，判断下就可以进行过滤。</p>
<p><span id="more-2805"></span></p>
<h2>自己实现</h2>
<p>一开始，自己新建一个类用一个HashMap去保存所有的url path即可，在controller中的static域或者构造方法里，添加本controller中的所有url path即可。</p>
<p><strong>问题一：spring日志显示已经初始化完毕绑定了url了，但是并没有触发controller中的static域或者构造方法！</strong></p>
<p>不得不使用Class.forName()去强制触发static域。</p>
<p>原因：spring-servlet.xml中&lt;beans&gt;根标签加了default-lazy-init=&#8221;true&#8221;，所有controller在有真正http请求到来才会被访问到！</p>
<p><strong>问题二：spring日志显示明明有url记录，为啥要再自己干？</strong></p>
<p>日志显示一个叫RequestMappingHandlerMapping的东西显然保存了url注册，拿到这个bean的实例应该就大功告成。</p>
<h2>通过RequestMappingHandlerMapping</h2>
<p><strong>问题三：在Filter里用WebApplicationContextUtils.getWebApplicationContext(ServletContext)的方法拿到ApplicationContext后，但是拿不到bean</strong></p>
<p>这个ApplicationContext里面根本没有任何bean！</p>
<p>原因：这个ApplicationContext是<code class="markdown_inline_code">针对applicationContext.xml建出来的Root ApplicationContext</code>，而<code class="markdown_inline_code">Spring MVC根据spring-servlet.xml建立的是一个Child ApplicationContext</code></p>
<p><strong>问题四：在Filter里无法获取到spring-servlet.xml建立的Child ApplicationContext</strong></p>
<p>因为通过Root ApplicationContext拿不到Children，也没有任何API可以直接获取Child ApplicationContext。</p>
<p>原因：Filter是直接注册在web.xml中的，并不归Spring管理，Spring MVC根据spring-servlet.xml建立ApplicationContext只能注入到同一个Spring容器中的bean，比如所有controller中。</p>
<p>于是新建一个类交给Spring管理（在spring-servlet.xml中定义bean），然后<code class="markdown_inline_code">implements ApplicationContextAware</code>，就可以获得Spring MVC的ApplicationContext了，自然就拿到了RequestMappingHandlerMapping的bean，将其保存下来，通过判断<code class="markdown_inline_code">requestMappingHandlerMapping.getHandler(request) == null</code>即可知道是否为有效url path。</p>
<h4  class="related_post_title">看看 Spring</h4><ul class="related_post"><li>2013-08-10 -- <a target="_blank" href="http://blog.11034.org/2013-08/some_skills_in_java.html" title="学到的一些东西">学到的一些东西</a></li><li>2012-05-13 -- <a target="_blank" href="http://blog.11034.org/2012-05/java_play_framework.html" title="Java Play framework">Java Play framework</a></li><li>2011-03-29 -- <a target="_blank" href="http://blog.11034.org/2011-03/java_encoding.html" title="Java编码的那些事儿">Java编码的那些事儿</a></li><li>2011-03-15 -- <a target="_blank" href="http://blog.11034.org/2011-03/spring_hibernate_annotation.html" title="SpringMVC+Hibernate的一些配置">SpringMVC+Hibernate的一些配置</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-03/spring_mvc_applicationcontext.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Java最外层异常的抓取要用Throwable</title>
		<link>http://blog.11034.org/2016-03/catch_throwable.html</link>
		<comments>http://blog.11034.org/2016-03/catch_throwable.html#comments</comments>
		<pubDate>Tue, 22 Mar 2016 13:26:26 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2803</guid>
		<description><![CDATA[3月以来，从来运行正常的link无故崩了好多次，现象：无异常日志，用jstack看主线程挂了进程还在，主线程最 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>3月以来，从来运行正常的link无故崩了好多次，现象：无异常日志，用jstack看主线程挂了进程还在，主线程最外层有catch异常，但是并没有打印日志，实在匪夷所思。</p>
<p><span id="more-2803"></span></p>
<p>先是怀疑socket连接，通过系统监控看到socket连接数在1w左右，进程ulimit 65536，无内核相关日志。</p>
<p>这时候用jstat分析内存使用，发现link的YGC和FGC增长非常迅速，而且老年代常年在90%以上经常在95%以上，几秒到十几秒一次FGC，一秒2-3次YGC。显然内存已经在崩溃边缘，这时候就想到了可能因为内存不够抛出了OutOfMemoryError，因为是Error，catch Exception无法捕捉！但是在测试机手动模拟抛出OutOfMemoryError，却引发了整个进程的挂掉而不仅仅是主线程。不过这里提供了思路，应该是抛出了某种Error。</p>
<p>无奈对link进行修改重新编译打包（毕竟这个工程从来没有动过，而且非常关键重要，一直不敢改动），将最外层的catch Exception改为catch Throwable，还加上了UncaughtExceptionHandler的实现来捕获主线程可能未捕捉的异常。同时将link的内存从默认值（48g的机器，应该默认为12g）直接加大到24g。运行后一段时间，内存使用情况过度良好，完全没有FGC了，YGC也很少，看来是吓怕了给了太多了内存了。本来以为加大了内存后不会再崩了，坚持了很久之后终于又崩了一次，抓取到了Error找到了原因！</p>
<p>原来是因为热部署修改app时，修改了与link共用的common.jar包后引起的<code class="markdown_inline_code">java.lang.NoClassDefFoundError</code>，怪不得了。其实一直就有在怀疑这个在app中会出现的问题，怎么在link中一直没出现过，果然不是偶然啊，虽然在近期才大量爆发之前一直安然无恙也是比较奇怪。引起NoClassDefFoundError的类，与上次部署时修改的一个类处于同一个package下，不知道是不是直接原因。</p>
<p><code class="markdown_inline_code">最外层异常的抓取要用Throwable</code>啊！</p>
<p>至于Java进程运行后替换jar包引起的NoClassDefFoundError问题，值得具体再研究！</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2011-01-01 -- <a target="_blank" href="http://blog.11034.org/2011-01/wp-sns-share_2-0.html" title="wp-sns-share更新2.0">wp-sns-share更新2.0</a></li><li>2013-06-23 -- <a target="_blank" href="http://blog.11034.org/2013-06/shida.html" title="院版十大了，那就XYT一下~">院版十大了，那就XYT一下~</a></li><li>2010-08-05 -- <a target="_blank" href="http://blog.11034.org/2010-08/java_io_nio.html" title="Java学习之IO与NIO篇">Java学习之IO与NIO篇</a></li><li>2010-05-09 -- <a target="_blank" href="http://blog.11034.org/2010-05/%e7%bb%8d%e5%85%b4%e6%b8%b8.html" title="母亲节，绍兴游">母亲节，绍兴游</a></li><li>2010-03-18 -- <a target="_blank" href="http://blog.11034.org/2010-03/my_blog.html" title="–Flyぁ梦–  的博客">–Flyぁ梦–  的博客</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-03/catch_throwable.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Java客户端Socket在服务端重启后的异常情况处理</title>
		<link>http://blog.11034.org/2016-03/java_client_socket_exceptions.html</link>
		<comments>http://blog.11034.org/2016-03/java_client_socket_exceptions.html#comments</comments>
		<pubDate>Thu, 17 Mar 2016 11:25:04 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Socket]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2799</guid>
		<description><![CDATA[碰到场景如下：Java服务器端A（使用NIO的异步IO方式），Java客户端B（使用普通IO的同步方式），在服 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>碰到场景如下：Java服务器端A（使用NIO的异步IO方式），Java客户端B（使用普通IO的同步方式），在服务端A重启后，B-&gt;A的Socket毫无疑问的断了，然后就会出现一些异常情况，分析一下。</p>
<p><span id="more-2799"></span></p>
<h2>同步IO客户端既发也收</h2>
<p>在服务端A重启后，B再次发送数据到A（虽然socket连接已断，但这个过程无异常，但其实数据并无法发送），然后B从A读数据的结果中，<code class="markdown_inline_code">inputStream.read(byte[] array)会返回-1</code>，表示end-of-file。</p>
<p>这种情况就比较好处理，在底层获取到end-of-file后抛出个异常，上层捕获异常后通知底层进行新建socket重连，然后上层再重发数据再读回包即可。</p>
<h2>同步IO客户端只发不收</h2>
<p>在服务端A重启后，B尝试第一次发送数据到A（虽然socket连接已断，但这个过程无异常，检测socket.isConnected()=true或者socket.isClosed()=false也都显示正常，但其实数据并无法发送），当B第二次再发送数据到A，在outputStream.write中，会报异常<code class="markdown_inline_code">java.net.SocketException: Broken pipe</code>。</p>
<p>所以通过抓取异常重连重发的办法，要在第二次发送失败时才能被发现并work。</p>
<h2>客户端不发只收</h2>
<p>这种情况在实践中感觉比较少见，即使业务逻辑如此，客户端应该也要发送心跳包到服务器，然后就是 既发也收 了。</p>
<h2>异步IO客户端</h2>
<p>如果客户端也是NIO的异步IO方式，在服务端A正常关闭时，客户端B会接收到SelectionKey.isReadable() == true的通知，然后从SocketChannel中read返回-1值，表示EOF，就可以处理关闭链接，然后下一次write的时候就可以进行重连了。</p>
<p>如果是服务端非正常关闭，比如断电、宕机等情况，有待验证，理论上应该不会返回read通知也不会返回-1的结果。</p>
<h4  class="related_post_title">看看 Socket</h4><ul class="related_post"><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-03/java_client_socket_exceptions.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>警惕long值转化为int，比如在Comparator接口中</title>
		<link>http://blog.11034.org/2016-03/long2int.html</link>
		<comments>http://blog.11034.org/2016-03/long2int.html#comments</comments>
		<pubDate>Wed, 02 Mar 2016 08:39:52 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[排序]]></category>
		<category><![CDATA[时间戳]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2786</guid>
		<description><![CDATA[long值转为int会造成溢出大家都知道，很多时候却会忽视已发bug，造成明明的正值变为了负值（因为int最高 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>long值转为int会造成溢出大家都知道，很多时候却会忽视已发bug，造成明明的正值变为了负值（因为int最高位非0，long值大于int最大值<code class="markdown_inline_code">2^31-1=2147483647</code>）。</p>
<p>此次出错的地方在使用<code class="markdown_inline_code">java.util.Collections.sort</code>中使用<code class="markdown_inline_code">Comparator</code>接口中，因为compare方法通过返回int值来比较大小，而逻辑比较大小是通过比较时间戳long值按最近时间排序，一开始不注意就直接把时间戳long值差给直接转为int值返回了，造成了可能的排序错误（有趣的是只需要<code class="markdown_inline_code">24天的毫秒值就会超过int的最大值</code>）。</p>
<p><span id="more-2786"></span></p>
<p>错误代码如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #003399;">Collections</span>.<span style="color: #006633;">sort</span><span style="color: #009900;">&#40;</span>list, <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Comparator</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
	@Override
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">int</span> compare<span style="color: #009900;">&#40;</span>Message o1, Message o2<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>o2.<span style="color: #006633;">timestamp</span> <span style="color: #339933;">-</span> o1.<span style="color: #006633;">timestamp</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// wrong</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>显然，我们只要简单的比较2个时间戳long值，返回1和-1即可。</p>
<p>修正代码如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #003399;">Collections</span>.<span style="color: #006633;">sort</span><span style="color: #009900;">&#40;</span>list, <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Comparator</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
	@Override
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">int</span> compare<span style="color: #009900;">&#40;</span>Message o1, Message o2<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>o2.<span style="color: #006633;">timestamp</span> <span style="color: #339933;">&gt;</span> o1.<span style="color: #006633;">timestamp</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">else</span><span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2010-12-14 -- <a target="_blank" href="http://blog.11034.org/2010-12/play_music_in_wordpress.html" title="wordpress中播放音乐">wordpress中播放音乐</a></li><li>2011-07-01 -- <a target="_blank" href="http://blog.11034.org/2011-07/xiamen_travel.html" title="毕业季，厦门行">毕业季，厦门行</a></li><li>2016-02-16 -- <a target="_blank" href="http://blog.11034.org/2016-02/pal6.html" title="仙剑六-剧情视频感受">仙剑六-剧情视频感受</a></li><li>2012-09-11 -- <a target="_blank" href="http://blog.11034.org/2012-09/shendiao_xialv.html" title="读神雕侠侣">读神雕侠侣</a></li><li>2016-05-26 -- <a target="_blank" href="http://blog.11034.org/2016-05/tomcat_gzip.html" title="Tomcat开启gzip压缩">Tomcat开启gzip压缩</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-03/long2int.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>jdk中SimpleDateFormat的实例线程不安全</title>
		<link>http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html</link>
		<comments>http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html#comments</comments>
		<pubDate>Tue, 01 Mar 2016 03:59:34 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[多线程]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2779</guid>
		<description><![CDATA[线上服碰到诡异问题，解析一段无参数固定代码生成的时间字符串获得时间戳，不定概率出现解析异常，堆栈如下。 产生的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>线上服碰到诡异问题，解析一段无参数固定代码生成的时间字符串获得时间戳，不定概率出现解析异常，堆栈如下。</p>
<p>产生的原因，因为SimpleDateFormat每次new的代价比较高，固定new一个后重复使用。一开始并没有对输入字符串进行日志打印，就以为是输入字符串的错误，就难以定位到产生问题的原因。</p>
<p><span id="more-2779"></span></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="text" style="font-family:monospace;">[ERROR]2016-03-01 09:03:13,446, [Class]DateTimeUtils, getTimestampFromDateString error : 2013-02-18 09:43:18.0
java.lang.NumberFormatException: For input string: &quot;E.179E11&quot;
    ...
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
    at java.text.DateFormat.parse(DateFormat.java:335)
    at com.netease.popo.common.util.DateTimeUtils.getTimestampFromDateString(DateTimeUtils.java)
    ...</pre></td></tr></table></div>

<p>明明参数传入的字符串是<code class="markdown_inline_code">2013-02-18 09:43:18.0</code>，怎么报异常就变成了<code class="markdown_inline_code">E.179E11</code>（有时候报错是空字符串，有时候提示<code class="markdown_inline_code">multiple points</code>），就很容易联想到是多线程引起的问题了。</p>
<p>解决的办法：</p>
<ol>
<li>每次new SimpleDateFormat(dateTimeFormat).parse(time)，代价比较大</li>
<li><code class="markdown_inline_code">ThreadLocal</code>，给每个线程新建一个SimpleDateFormat对象</li>
<li>线程安全的第三方库，比如<code class="markdown_inline_code">joda-time</code>（org.joda.time）或<code class="markdown_inline_code">date4j</code>（www.date4j.net），前者使用更广泛</li>
</ol>
<ul>
<li>考虑到使用ThreadLocal需要自己管理且JDK对于日期时间API的弱势，引入一个第三方库对于长远来说也很有必要。</li>
<li>虽然易用性上可能不如date4j更简单，考虑到joda-time使用更广泛资料更多这样也保证了安全和稳定性以及后续的维护代价</li>
</ul>
<p>综上决定使用joda-time库。但是joda的parse功能并不强大，必须要结合具体的timeFormat才行，这点date4j要强大的多。</p>
<p>下面是根据时间字符串获取时间戳的一个例子，根据业务需求解析了几种常见的日期时间字符串格式。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> dateFormat <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;yyyy-MM-dd&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> dateTimeFormat <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;yyyy-MM-dd HH:mm:ss&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> DateTimeFormatter dateJFormat <span style="color: #339933;">=</span> DateTimeFormat.<span style="color: #006633;">forPattern</span><span style="color: #009900;">&#40;</span>dateFormat<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> DateTimeFormatter dateTimeJFormat <span style="color: #339933;">=</span> DateTimeFormat.<span style="color: #006633;">forPattern</span><span style="color: #009900;">&#40;</span>dateTimeFormat<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> DateTimeFormatter dbJFormat <span style="color: #339933;">=</span> DateTimeFormat.<span style="color: #006633;">forPattern</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;yyyy-MM-dd HH:mm:ss.S&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> DateTimeFormatter dateTimeMillJFormat <span style="color: #339933;">=</span> DateTimeFormat.<span style="color: #006633;">forPattern</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">long</span> getTimestampFromDateString<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> time<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>StringUtils.<span style="color: #006633;">isEmpty</span><span style="color: #009900;">&#40;</span>time<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">return</span> 0L<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #000066; font-weight: bold;">int</span> len <span style="color: #339933;">=</span> time.<span style="color: #006633;">length</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
		DateTime dateTime <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>len <span style="color: #339933;">==</span> <span style="color: #cc66cc;">10</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
			dateTime <span style="color: #339933;">=</span> DateTime.<span style="color: #006633;">parse</span><span style="color: #009900;">&#40;</span>time, dateJFormat<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">else</span> <span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>len <span style="color: #339933;">==</span> <span style="color: #cc66cc;">19</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
			dateTime <span style="color: #339933;">=</span> DateTime.<span style="color: #006633;">parse</span><span style="color: #009900;">&#40;</span>time, dateTimeJFormat<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">else</span> <span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>len <span style="color: #339933;">==</span> <span style="color: #cc66cc;">21</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
			dateTime <span style="color: #339933;">=</span> DateTime.<span style="color: #006633;">parse</span><span style="color: #009900;">&#40;</span>time, dbJFormat<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">else</span> <span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>len <span style="color: #339933;">==</span> <span style="color: #cc66cc;">23</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
			dateTime <span style="color: #339933;">=</span> DateTime.<span style="color: #006633;">parse</span><span style="color: #009900;">&#40;</span>time, dateTimeMillJFormat<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>dateTime <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">return</span> dateTime.<span style="color: #006633;">getMillis</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">else</span><span style="color: #009900;">&#123;</span>
			m_logger.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;getTimestampFromDateString joda miss : &quot;</span> <span style="color: #339933;">+</span> time<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">SimpleDateFormat</span><span style="color: #009900;">&#40;</span>dateTimeFormat<span style="color: #009900;">&#41;</span>.<span style="color: #006633;">parse</span><span style="color: #009900;">&#40;</span>time<span style="color: #009900;">&#41;</span>.<span style="color: #006633;">getTime</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">Exception</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		m_logger.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;getTimestampFromDateString error : &quot;</span> <span style="color: #339933;">+</span> time, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">return</span> 0L<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<h4  class="related_post_title">看看 多线程</h4><ul class="related_post"><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li><li>2012-10-01 -- <a target="_blank" href="http://blog.11034.org/2012-10/hashmap_concurrent_bug.html" title="HashMap多线程中引发的死循环">HashMap多线程中引发的死循环</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Java项目无法加载最新的class</title>
		<link>http://blog.11034.org/2015-12/loading_error_class.html</link>
		<comments>http://blog.11034.org/2015-12/loading_error_class.html#comments</comments>
		<pubDate>Tue, 22 Dec 2015 06:55:09 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2759</guid>
		<description><![CDATA[问题 一个Java项目，无论怎么替换jar包Jar1，运行中某个类A无法加载到最新的class，一直以替换前的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>问题</h2>
<p>一个Java项目，无论怎么替换jar包Jar1，运行中某个类A无法加载到最新的class，一直以替换前的旧版本运行导致报错；而Jar1中新加入的类B可以被正确执行，每次替换jar包都能加载到B最新的class。<span id="more-2759"></span></p>
<h2>背景</h2>
<p>此Java项目依赖于2个目录加载多个jar包，bin目录和lib目录（bin优先于lib，因为在classpath中靠前），正常情况下bin目录中有1个jar包，lib目录中有很多jar包，此次替换的jar包Jar1位于lib目录。</p>
<h2>原因</h2>
<p>通过打印类A的class的加载路径找到答案</p>
<p><code class="markdown_inline_code">class.getProtectionDomain().getCodeSource().getLocation()</code></p>
<p>上一次替换Jar1时，将更新的Jar1误多传入到bin目录下一份，当然lib目录下也替换了。启动，没有问题，此时系统加载的是bin下的Jar1。</p>
<p>这次再替换Jar1时，就因为优先加载了bin目录下的old-Jar1，覆盖了lib目录下的new-Jar1，导致类A新的class无法被加载，而类B因为是新加入的类，却可以被加载到。</p>
<h2>解决</h2>
<p>删除bin目录下的old-Jar1包即可</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2012-01-01 -- <a target="_blank" href="http://blog.11034.org/2012-01/summary_2011.html" title="2011年的总结">2011年的总结</a></li><li>2016-03-01 -- <a target="_blank" href="http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html" title="jdk中SimpleDateFormat的实例线程不安全">jdk中SimpleDateFormat的实例线程不安全</a></li><li>2013-02-14 -- <a target="_blank" href="http://blog.11034.org/2013-02/wp_zjuem_in_wordpress.html" title="wp-zjuem，zju cc98表情系统 in wordpress">wp-zjuem，zju cc98表情系统 in wordpress</a></li><li>2016-09-10 -- <a target="_blank" href="http://blog.11034.org/2016-09/zhangjiajie.html" title="偶遇张家界，武陵源和天门山">偶遇张家界，武陵源和天门山</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-12/loading_error_class.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Linux下搭建Tomcat环境</title>
		<link>http://blog.11034.org/2015-08/tomcat_linux.html</link>
		<comments>http://blog.11034.org/2015-08/tomcat_linux.html#comments</comments>
		<pubDate>Fri, 28 Aug 2015 07:21:34 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[Tomcat]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2507</guid>
		<description><![CDATA[多年没有碰Java Web了，之前也一直在Windows下玩耍。在Linux下跑起Tomcat也折腾了一阵。  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>多年没有碰Java Web了，之前也一直在Windows下玩耍。在Linux下跑起Tomcat也折腾了一阵。</p>
<p>看Apache Tomcat官网，已经有Tomcat 8.x（据说支持Oracle Java 7.0标准），7.x和6.x，习惯还是使用6.0.44版本。</p>
<p><span id="more-2507"></span></p>
<h2>设置环境变量</h2>
<p>打开<code class="markdown_inline_code">~/.profile</code>文件，或者/etc/profile文件</p>
<pre class="markdown_pre"><code>JAVA_HOME=/usr/.../java
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
CATALINA_HOME=/home/.../tomcat
export JAVA_HOME PATH CLASSPATH CATALINA_HOME</code></pre>
<p>使用<code class="markdown_inline_code">source ~/.profile</code>命令重新加载文件（或重新登陆ssh即可），查看命令<code class="markdown_inline_code">echo $JAVA_HOME</code></p>
<h2>给Tomcat脚本加执行权限</h2>
<p>进入<code class="markdown_inline_code">$CATALINA_HOME/bin</code>，<code class="markdown_inline_code">chmod +x *.sh</code></p>
<p>然后<code class="markdown_inline_code">./startup.sh</code>，应该就能启动了</p>
<p>启动成功显示：</p>
<pre class="markdown_pre"><code>Using CATALINA_BASE: /home/.../apache-tomcat-6.0.44
Using CATALINA_HOME: /home/.../apache-tomcat-6.0.44
Using CATALINA_TMPDIR: /home/.../apache-tomcat-6.0.44/temp
Using JRE_HOME: /usr/.../java
Using CLASSPATH: /home/.../apache-tomcat-6.0.44/bin/bootstrap.jar</code></pre>
<p>用netstat -ntlp显示：</p>
<pre class="markdown_pre"><code>tcp6 0 0 :::8009 :::* LISTEN 25424/java    # AJP服务
tcp6 0 0 :::8080 :::* LISTEN 25424/java    # HTTP服务
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 25424/java  # 关闭命令接收服务</code></pre>
<p>修改Tomcat启动端口，在conf/server.xml，修改<code class="markdown_inline_code">Connector port="8080"</code>中的端口号即可</p>
<h2>以根目录启动</h2>
<p>可以在conf/server.xml中，修改Host标签中添加一行，docBase指向含有WEB-INF的目录即可</p>
<p><code class="markdown_inline_code">&lt;Context path="" docBase="/home/.../app" debug="0" reloadable="true" /&gt;</code></p>
<h4  class="related_post_title">看看 linux , Tomcat</h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-06-08 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_shutdown.html" title="Tomcat监听shutdown释放数据库连接池">Tomcat监听shutdown释放数据库连接池</a></li><li>2016-06-06 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_https.html" title="Tomcat启用https服务">Tomcat启用https服务</a></li><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2013-05-20 -- <a target="_blank" href="http://blog.11034.org/2013-05/coyote.html" title="Tomcat的Connector：Coyote">Tomcat的Connector：Coyote</a></li></ul><h4 class="related_post_title">看看 Java </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-08-05 -- <a target="_blank" href="http://blog.11034.org/2016-08/thread_stop.html" title="线程清理">线程清理</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/futuretask.html" title="FutureTask简单分析和用法">FutureTask简单分析和用法</a></li><li>2016-06-21 -- <a target="_blank" href="http://blog.11034.org/2016-06/semaphore.html" title="Semaphore简单分析">Semaphore简单分析</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-08/tomcat_linux.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Liunx下搭建Rails和Nginx环境</title>
		<link>http://blog.11034.org/2015-08/rails_nginx.html</link>
		<comments>http://blog.11034.org/2015-08/rails_nginx.html#comments</comments>
		<pubDate>Wed, 12 Aug 2015 05:21:26 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[nginx]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2504</guid>
		<description><![CDATA[用rails开发轻量级web，效率最高！ 安装rvm http://www.rvm.io/ \curl -sS [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>用rails开发轻量级web，效率最高！</p>
<p><span id="more-2504"></span></p>
<h2>安装rvm</h2>
<p><code class="markdown_inline_code">http://www.rvm.io/</code></p>
<pre class="markdown_pre"><code>\curl -sSL https://get.rvm.io | bash -s stable

echo "source $HOME/.rvm/scripts/rvm" &gt;&gt; ~/.bash_profile

source /etc/profile

然后就可以在命令行中使用rvm命令了</code></pre>
<h2>安装ruby</h2>
<p>安装rvm后安装ruby下载缓慢，手动在ruby.taobao.org下载ruby-1.9.3，将安装包放在rvm/archives下即可</p>
<p><code class="markdown_inline_code">rvm install 1.9.3-p545</code> （如果不指定p版本，则不一定会使用安装包，而去下载网上最新版本）</p>
<p><code class="markdown_inline_code">which ruby</code> 可以找到当前版本ruby版本和位置</p>
<p>按照http://ruby.taobao.org/的方法，将gem的source改为<code class="markdown_inline_code">https://ruby.taobao.org/</code></p>
<p>这里要大大赞一下taobao，为国内开发者提供了便利，然后严重鄙视和谴责GFW</p>
<pre class="markdown_pre"><code>gem sources --remove https://rubygems.org/
gem sources -a https://ruby.taobao.org/
gem sources -l
*** CURRENT SOURCES ***

https://ruby.taobao.org
# 请确保只有 ruby.taobao.org</code></pre>
<h2>安装mysql</h2>
<p><code class="markdown_inline_code">apt-get install mysql-server</code></p>
<p><code class="markdown_inline_code">apt-get install libmysqlclient-dev</code></p>
<p><code class="markdown_inline_code">gem install mysql2 -v=0.2.24</code> <code class="markdown_inline_code">gem install activerecord-mysql2-adapter</code></p>
<h2>安装rails</h2>
<p><code class="markdown_inline_code">gem install rails -v=3.0.20</code> 3.2版本以上使用了很多新的东西，包括要安装nodejs等环境比较麻烦</p>
<p><code class="markdown_inline_code">rails new demo --skip-bundle</code></p>
<pre class="markdown_pre"><code>如果提示缺少必要gem的话，可以使用bundle自动安装
cd demo
bundle config mirror.https://rubygems.org https://ruby.taobao.org
bundle install</code></pre>
<p><code class="markdown_inline_code">rails server</code>或者<code class="markdown_inline_code">rails s</code> 即可启动自带的WEBrick web server</p>
<h2>rails连接mysql</h2>
<p>在mysql中新建数据库</p>
<p>修改<code class="markdown_inline_code">config/database.yml</code></p>
<p>将adapter类型修改为<code class="markdown_inline_code">mysql2</code></p>
<pre class="markdown_pre"><code>production:
adapter: mysql2
database: db_name
username: db_user
password: db_pawd
host: 127.0.0.1
encoding: utf8
socket: /var/run/mysqld/mysqld.sock</code></pre>
<p>修改demo/Gemfile，注释#gem &#8216;sqlite3&#8217;，添加<code class="markdown_inline_code">gem 'mysql2', '&lt; 0.3'</code></p>
<p>然后写一个最简单的migrate文件用以测试，执行<code class="markdown_inline_code">RAILS_ENV=production db:migrate</code></p>
<p>不成功的原因：<br />
若提示无法加载libmysqlclient.18.dylib，是因为/usr/lib下没有这个文件，作一个软链接即可，前提是找到本机mysql安装位置：<br />
<code class="markdown_inline_code">ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/libmysqlclient.18.dylib</code></p>
<h2>安装pcre和nginx</h2>
<p>源码安装，将nginx包解压到/opt/nginx-x.x.x后，不要将/opt/nginx-x.x.x改名为/opt/nginx，因为/opt/nginx目录会在编译后自动生成。然后不要删除源码目录，安装Passenger还会用到。</p>
<p><code class="markdown_inline_code">这里不需要自己编译安装Nginx</code>，因为安装Passenger的时候会需要编译安装一次</p>
<p>安装PCRE：<code class="markdown_inline_code">apt-get install libpcre3 libpcre3-dev</code></p>
<pre class="markdown_pre"><code>若自己编译nginx，方法如下：
./configure --prefix='/opt/nginx' --with-http_stub_status_module --with-http_sub_module --with-md5='/usr/lib' --with-sha1='/usr/lib --with-http_gzip_static_module' --with-http_ssl_module --with-cc-opt=-Wno-error --with-http_secure_link_module
# 若是手动编译安装PCRE，则需要加上 --with-pcre=/opt/pcre-8.36
make
make install
然后/opt/nginx目录就出来啦，安装完毕</code></pre>
<h2>安装Passenger</h2>
<p>首先是<code class="markdown_inline_code">gem install passenger</code> ，</p>
<p>然后进到gem的Passenger/bin目录中（可能在/usr/local/rvm/gems/ruby-1.9.3-p551/gems/passenger-5.0.16/），执行<code class="markdown_inline_code">passenger-install-nginx-module</code>脚本，选择第二种自定义安装nginx（第一种自动下载、编译、安装nginx），通过命令行输入nginx源码目录即可，之后都默认。</p>
<p>最后按照安装Passenger完毕给予的提示，配置nginx的conf/nginx.conf。</p>
<pre class="markdown_pre"><code>http {
passenger_root /usr/local/rvm/gems/ruby-1.9.3-p551/gems/passenger-5.0.16;
passenger_ruby /usr/local/rvm/gems/ruby-1.9.3-p551/wrappers/ruby;

server {
listen 80;
server_name localhost;
passenger_enabled on;
rails_env production;

location / {
root rails_project/public;
}
}
}</code></pre>
<h4  class="related_post_title">看看 linux , MySQL , nginx</h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/64bits_linux_arena_memory.html" title="64位Linux下Java进程堆外内存迷之64M问题">64位Linux下Java进程堆外内存迷之64M问题</a></li><li>2015-08-28 -- <a target="_blank" href="http://blog.11034.org/2015-08/tomcat_linux.html" title="Linux下搭建Tomcat环境">Linux下搭建Tomcat环境</a></li><li>2013-06-23 -- <a target="_blank" href="http://blog.11034.org/2013-06/shida.html" title="院版十大了，那就XYT一下~">院版十大了，那就XYT一下~</a></li><li>2011-03-15 -- <a target="_blank" href="http://blog.11034.org/2011-03/spring_hibernate_annotation.html" title="SpringMVC+Hibernate的一些配置">SpringMVC+Hibernate的一些配置</a></li></ul><h4 class="related_post_title">看看 ruby on rails </h4><ul class="related_post"><li>2015-04-09 -- <a target="_blank" href="http://blog.11034.org/2015-04/json_ascii.html" title="JSON中出现解析错误的原因之一：不可打印字符">JSON中出现解析错误的原因之一：不可打印字符</a></li><li>2014-12-21 -- <a target="_blank" href="http://blog.11034.org/2014-12/ruby_encoding.html" title="Ruby字符串的编码">Ruby字符串的编码</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-10-04 -- <a target="_blank" href="http://blog.11034.org/2014-10/rails-has_many.html" title="Rails中has_many等的原理">Rails中has_many等的原理</a></li><li>2014-07-28 -- <a target="_blank" href="http://blog.11034.org/2014-07/active_support_235.html" title="简析Rails：active_support 2.3.5">简析Rails：active_support 2.3.5</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-08/rails_nginx.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>域名备案和博客迁移至国内</title>
		<link>http://blog.11034.org/2015-07/beian.html</link>
		<comments>http://blog.11034.org/2015-07/beian.html#comments</comments>
		<pubDate>Wed, 29 Jul 2015 03:46:54 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[博客建站]]></category>
		<category><![CDATA[域名]]></category>
		<category><![CDATA[备案]]></category>
		<category><![CDATA[虚拟主机]]></category>
		<category><![CDATA[阿里云]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2500</guid>
		<description><![CDATA[一直用的dreamhost国外的主机，前些日子感觉越来越不好使，速度慢不说，经常根本无法连接上，FTP更是慢大 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>一直用的dreamhost国外的主机，前些日子感觉越来越不好使，速度慢不说，经常根本无法连接上，FTP更是慢大部分时候无法上传和下载。然后国内环境又碰到好多网站设置需要域名备案号的，不然无法使用某些功能，比如国内的广告商、微信网页安全授权等。</p>
<p>前些日子，在微博上偶然发现阿里云虚拟主机免费使用，赶紧尝试了一个，还挺好用的，毕竟咱博客就是个小网站足够了。于是就开始了域名备案和数据迁移。</p>
<p>虽然有点恶心域名备案这个事情，但想想咱也不干什么坏事，毕竟国内网速快啊。<span id="more-2500"></span></p>
<h2>域名备案</h2>
<p>在阿里云的备案中心，还挺方便的，主要有2个步骤比较麻烦。一个是下载网站提供的一个申请表格，打印3份签字后快递送到阿里云指定地点，也就是这边需要缴一个快递费用。另一个就是拍照，备案网站有提供线下的免费拍照服务点，杭州市区和萧山市区都有，这样应该是最方便的，萧山这个周六也可以去办，就在育才路汽车站后面。如果线下不方便，就让阿里云邮寄蓝色的幕布过来然后自己拍照。</p>
<p>这两步搞定，最后就是国家工信部的审核了，一般没啥大问题，我这审核了一周。</p>
<p>PS：在备案过程中，域名必须出于不可用状态（或者显示空内容）</p>
<p>PPS：备案仅仅只是个对域名和个人信息的验证绑定，网站具体内容根本都是不知道的（因为没有备案网站不能上线）；当然后期被发现网站有问题，就会被叫去喝茶了&#8230;</p>
<h2>博客迁移</h2>
<p>包括数据迁移和域名指向，还是搞了快2天才搞定，刷新DNS比较慢。</p>
<p>阿里云这个免费虚拟主机不支持二级域名绑定到具体目录，只支持域名映射到IP，然后显示根目录下的内容。然后默认情况下，虚拟主机不支持wordpress的url rewrite，需要手动添加.htaccess文件才可以。</p>
<p>网站容量1G，数据库容量50M，每月流量10G，对于小博客来说应该基本够用了。</p>
<p>现在博客速度在wifi下快的多了，5秒之内就可以打开 <img src="http://blog.11034.org/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h4  class="related_post_title">看看 域名 , 备案 , 虚拟主机 , 阿里云</h4><ul class="related_post"><li>2010-12-04 -- <a target="_blank" href="http://blog.11034.org/2010-12/google_page_speed.html" title="加速你的网站，Google page speed">加速你的网站，Google page speed</a></li><li>2010-11-28 -- <a target="_blank" href="http://blog.11034.org/2010-11/domain_change_to_stariy-org.html" title="域名更换">域名更换</a></li></ul><h4 class="related_post_title">看看 博客建站 </h4><ul class="related_post"><li>2011-06-08 -- <a target="_blank" href="http://blog.11034.org/2011-06/dreamhost.html" title="新空间Dreamhost">新空间Dreamhost</a></li><li>2011-03-18 -- <a target="_blank" href="http://blog.11034.org/2011-03/blog_first_anniversary.html" title="庆祝博客上线一周年">庆祝博客上线一周年</a></li><li>2011-01-08 -- <a target="_blank" href="http://blog.11034.org/2011-01/search_engine.html" title="2个月的搜索引擎状况">2个月的搜索引擎状况</a></li><li>2010-12-04 -- <a target="_blank" href="http://blog.11034.org/2010-12/google_page_speed.html" title="加速你的网站，Google page speed">加速你的网站，Google page speed</a></li><li>2010-11-28 -- <a target="_blank" href="http://blog.11034.org/2010-11/domain_change_to_stariy-org.html" title="域名更换">域名更换</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-07/beian.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>JSON中出现解析错误的原因之一：不可打印字符</title>
		<link>http://blog.11034.org/2015-04/json_ascii.html</link>
		<comments>http://blog.11034.org/2015-04/json_ascii.html#comments</comments>
		<pubDate>Thu, 09 Apr 2015 08:59:36 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[json]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2407</guid>
		<description><![CDATA[JSON规范规定，字符串需要用Unicode传输，而对于ASCII码中的前32个不可打印字符（第33个字符为空 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>JSON规范规定，字符串需要用Unicode传输，而对于ASCII码中的前32个不可打印字符（第33个字符为空格）应该转化为明文的转义模式，比如换行符0A：&#8221;\n&#8221;，出现在json中就应该是明文的\n，所以内部表示就应该是&#8221;\\n&#8221;。</p>
<p>所以假如一个json字符串中出现了01-1F这31个字符（00空字符倒是不会出现错误），json就会解析错误。</p>
<p><span id="more-2407"></span></p>
<p>Ruby on Rails中的to_json方法，默认并没有将这31个字符给正确转义为json格式，会引起错误。</p>
<p>用Rails的console举例：</p>
<div id="attachment_2408" style="width: 495px" class="wp-caption aligncenter"><img class="size-full wp-image-2408" alt="ASCII码转json测试" src="http://blog.11034.org/wp-content/uploads/2015/04/asc.png" width="485" height="268" /><p class="wp-caption-text">ASCII码转json测试</p></div>
<p>所以可能要手动去删除1-31号不可显示的ASCII码，除了\n\r\t之外，一般来说这些字符对于内容显示上不存在作用。</p>
<p>补充一点，ASCII表的最后一个字符127位为DEL (delete)，虽然可能也算不可打印字符，不过这个字符不会引起json解析错误。</p>
<p>附上ASCii码表：<a href="http://www.asciima.com/">http://www.asciima.com/</a></p>
<p>一种解决办法：对to_json解析后的字符串进行字符过滤</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;">    <span style="color:#9966CC; font-weight:bold;">def</span> to_json<span style="color:#006600; font-weight:bold;">&#40;</span>options = <span style="color:#0000FF; font-weight:bold;">nil</span><span style="color:#006600; font-weight:bold;">&#41;</span>
        json_str = <span style="color:#0066ff; font-weight:bold;">@data</span>.<span style="color:#9900CC;">to_json</span><span style="color:#006600; font-weight:bold;">&#40;</span>options<span style="color:#006600; font-weight:bold;">&#41;</span>
        bytes = <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
        json_str.<span style="color:#9900CC;">each_byte</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>c<span style="color:#006600; font-weight:bold;">|</span>
            <span style="color:#9966CC; font-weight:bold;">next</span> <span style="color:#9966CC; font-weight:bold;">if</span> c <span style="color:#006600; font-weight:bold;">&gt;</span>= <span style="color:#006666;">1</span> <span style="color:#006600; font-weight:bold;">&amp;&amp;</span> c <span style="color:#006600; font-weight:bold;">&lt;</span>= <span style="color:#006666;">31</span>
            bytes <span style="color:#006600; font-weight:bold;">&lt;&lt;</span> c
        <span style="color:#9966CC; font-weight:bold;">end</span>
        <span style="color:#0000FF; font-weight:bold;">return</span> bytes.<span style="color:#9900CC;">pack</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'C*'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2012-11-25 -- <a target="_blank" href="http://blog.11034.org/2012-11/photos.html" title="玉泉、白堤、北山路">玉泉、白堤、北山路</a></li><li>2012-02-10 -- <a target="_blank" href="http://blog.11034.org/2012-02/wp-sns-share_2-5.html" title="wp_sns_share更新2.5">wp_sns_share更新2.5</a></li><li>2015-10-12 -- <a target="_blank" href="http://blog.11034.org/2015-10/longkeng.html" title="我在龙坑吃了个惊">我在龙坑吃了个惊</a></li><li>2016-03-17 -- <a target="_blank" href="http://blog.11034.org/2016-03/java_client_socket_exceptions.html" title="Java客户端Socket在服务端重启后的异常情况处理">Java客户端Socket在服务端重启后的异常情况处理</a></li><li>2016-09-10 -- <a target="_blank" href="http://blog.11034.org/2016-09/fenghuang.html" title="最美古城，湘西凤凰">最美古城，湘西凤凰</a></li></ul><h4 class="related_post_title">看看 Javascript , ruby on rails </h4><ul class="related_post"><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2014-12-21 -- <a target="_blank" href="http://blog.11034.org/2014-12/ruby_encoding.html" title="Ruby字符串的编码">Ruby字符串的编码</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-10-04 -- <a target="_blank" href="http://blog.11034.org/2014-10/rails-has_many.html" title="Rails中has_many等的原理">Rails中has_many等的原理</a></li><li>2014-07-28 -- <a target="_blank" href="http://blog.11034.org/2014-07/active_support_235.html" title="简析Rails：active_support 2.3.5">简析Rails：active_support 2.3.5</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-04/json_ascii.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Cordova native API和开源插件</title>
		<link>http://blog.11034.org/2015-03/cordova_plugins.html</link>
		<comments>http://blog.11034.org/2015-03/cordova_plugins.html#comments</comments>
		<pubDate>Mon, 30 Mar 2015 03:11:48 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[APP]]></category>
		<category><![CDATA[Cordova]]></category>
		<category><![CDATA[二维码]]></category>
		<category><![CDATA[插件]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2386</guid>
		<description><![CDATA[开发Cordova的APP使用到下面几个原生或者开源第三方的插件，很好用 1.Notification org [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>开发Cordova的APP使用到下面几个原生或者开源第三方的插件，很好用</p>
<p>1.Notification</p>
<p style="padding-left: 30px;">org.apache.cordova.dialogs和org.apache.cordova.vibration</p>
<p style="padding-left: 30px;">取代原生alert（不能控制title），提供丰富的alert、confirm等API</p>
<p>2.InAppBrower</p>
<p style="padding-left: 30px;">org.apache.cordova.inappbrower</p>
<p style="padding-left: 30px;">可以在APP内部新开一个页面打开浏览器，可以关闭</p>
<p>3.Network Infomation</p>
<p style="padding-left: 30px;">org.apache.cordova.network-infomation</p>
<p style="padding-left: 30px;">检查网络类型和状态，可以检查出当前网络是2G/3G/4G/WIFI，有事件监听断网和连网</p>
<p>4.Camera</p>
<p style="padding-left: 30px;">org.apache.cordova.camera</p>
<p style="padding-left: 30px;">调用摄像头或从文件夹中选取，可以获取图片base64后的数据</p>
<p>5.Screen Orientation</p>
<p style="padding-left: 30px;">net.yoik.cordova.plugins.screentorientation</p>
<p style="padding-left: 30px;">可以使用screen.lock等API， 锁定或者解锁屏幕转动</p>
<p>6.BarcodeScanner</p>
<p style="padding-left: 30px;">com.phonegap.plugins.barcodescanner</p>
<p style="padding-left: 30px;">https://github.com/wildabeast/BarcodeScanner/    主页地址</p>
<p style="padding-left: 30px;">可以扫描二维码等，获取内部的文本信息和文本类型</p>
<p>&nbsp;</p>
<h4  class="related_post_title">看看 Cordova , 二维码 , 插件</h4><ul class="related_post"><li>2013-02-14 -- <a target="_blank" href="http://blog.11034.org/2013-02/wp_zjuem_in_wordpress.html" title="wp-zjuem，zju cc98表情系统 in wordpress">wp-zjuem，zju cc98表情系统 in wordpress</a></li><li>2010-12-05 -- <a target="_blank" href="http://blog.11034.org/2010-12/add_category_related_to_wp_related_posts.html" title="给wp-related-posts增加类目相关日志">给wp-related-posts增加类目相关日志</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-03/cordova_plugins.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>防止数据库数据重复的几种方法</title>
		<link>http://blog.11034.org/2015-01/prevent_db_duplicate.html</link>
		<comments>http://blog.11034.org/2015-01/prevent_db_duplicate.html#comments</comments>
		<pubDate>Wed, 07 Jan 2015 09:22:49 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Life in Coding]]></category>
		<category><![CDATA[同步]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2374</guid>
		<description><![CDATA[在某些数据库表中，比如记录的是一些关联关系，比如某个人关注了另外一个人，person_id和target_id [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在某些数据库表中，比如记录的是一些关联关系，比如某个人关注了另外一个人，person_id和target_id，这种数据表的数据如果出现了重复，可能会引起程序上其他地方的诡异bug，要尽量保持数据的唯一性。这种数据重复，要不就是插入数据时根本没有做存在检查，或者是可能由二次提交产生的比较麻烦的并发情况。</p>
<pre class="markdown_pre"><code>1. 代码层做同步控制，利用锁机制
2. 在数据查重时利用数据库事务 + SQL层加入排他锁，select * from xx where ... lock for update
3. 数据库层Unique检查，建表时加入unique的索引</code></pre>
<p>前两个方法在代码层面，比较容易控制，第三种方法最彻底但是出现冲突时候会报异常。</p>
<p>不过如果在高并发的情况下，使用第三种方式然后主动捕获异常，也是很不错的甚至是最好的。</p>
<h4  class="related_post_title">看看 同步 , 并发 , 数据库</h4><ul class="related_post"><li>2016-08-18 -- <a target="_blank" href="http://blog.11034.org/2016-08/java_concurrency_in_practice.html" title="读java concurrency in practice">读java concurrency in practice</a></li><li>2016-06-16 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantreadwritelock.html" title="ReentrantReadWriteLock简单分析">ReentrantReadWriteLock简单分析</a></li><li>2016-06-13 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantlock.html" title="可重入锁、ReentrantLock、AQS、Condition">可重入锁、ReentrantLock、AQS、Condition</a></li><li>2016-06-08 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_shutdown.html" title="Tomcat监听shutdown释放数据库连接池">Tomcat监听shutdown释放数据库连接池</a></li></ul><h4 class="related_post_title">看看 Life in Coding </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/jabber_and_xmpp.html" title="jabber和XMPP简述原理">jabber和XMPP简述原理</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-09-10 -- <a target="_blank" href="http://blog.11034.org/2014-09/dotnet_csharp_excel.html" title="记C#和Excel开发">记C#和Excel开发</a></li><li>2011-06-11 -- <a target="_blank" href="http://blog.11034.org/2011-06/google_doodle_guitar.html" title="2011-6-9 Google Doodle之电子琴">2011-6-9 Google Doodle之电子琴</a></li><li>2011-03-10 -- <a target="_blank" href="http://blog.11034.org/2011-03/learning_cs.html" title="学计算机的尼玛伤不起啊！！！！！！">学计算机的尼玛伤不起啊！！！！！！</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2015-01/prevent_db_duplicate.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Ruby字符串的编码</title>
		<link>http://blog.11034.org/2014-12/ruby_encoding.html</link>
		<comments>http://blog.11034.org/2014-12/ruby_encoding.html#comments</comments>
		<pubDate>Sun, 21 Dec 2014 09:54:31 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[ruby on rails]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2362</guid>
		<description><![CDATA[相比较于Java String的编码转换（先将String根据原字符集转化为字节数组，然后再用字节数组根据新字 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>相比较于Java String的编码转换（先将String根据原字符集转化为字节数组，然后再用字节数组根据新字符集拼装成新的字符串），Ruby的字符串编码转化省去了中间字节数组的过程，直接<code class="markdown_inline_code">string.encode(new_encoding)</code>即可。</p>
<p><span id="more-2362"></span></p>
<h2>字符串与字节串</h2>
<p>Ruby的字符串存储的都是字节，还有一个编码，根据这个编码对应的字符集来渲染字符。可以通过如下清除字符串的编码信息还原为裸的字节串：</p>
<pre class="markdown_pre"><code>str = '中'   #GBK编码
s1 = [str].pack('a*')   =&gt;   "\xD6\xD0"

str = '中'.encode('UTF-8')    #转化为UTF-8编码
s2 = [str].pack('a*')   =&gt;   "\xE4\xB8\xAD"

#Array.pack('a*')是用来向ASCII字符串结尾加入\0用的，所以会清除其他字符串的编码信息

s1.encoding.name =&gt; "ASCII-8BIT"</code></pre>
<p>现在得到的s1和s2字符串都是ASCII-8BIT的编码（或者说是没有带字符集的裸字节字符串），只要加上一个编码信息，就可以还原为GBK或者UTF-8的字符串，这种方式和Java也比较像了吧。</p>
<pre class="markdown_pre"><code>s1.force_encoding('GBK')          =&gt;     '中'
s2.force_encoding('UTF-8')         =&gt;     '中'</code></pre>
<p>Ruby中的字节数组，官方并没有给String直接的一个API来获取，但可以用过<code class="markdown_inline_code">str.unpack('c*') </code>或者<code class="markdown_inline_code">str.unpack('C*')</code>来获取，前者带符号位，后者不带符号位。</p>
<pre class="markdown_pre"><code>str = '中'   #GBK编码
[str].pack('c*')   =&gt;   [-42, -48]                  [str].pack('C*')   =&gt;  [214, 208]

str = '中'.encode('UTF-8')    #转化为UTF-8编码
[str].pack('c*')   =&gt;  [-28, -72, -83]              [str].pack('C*')   =&gt; [228, 184, 173]</code></pre>
<h2>UTF-8和Unicode</h2>
<p>不清除具体原因，在Ruby 1.9.3中，UTF-8字符串和Uniocde有一种特殊的自动转化方式（低于Ruby 1.9的版本，没有Unicode的支持），会自动进行转化，只要UTF-8字节串给加上了UTF-8的编码信息，字符串就自动转化为Unicode模式了。</p>
<pre class="markdown_pre"><code>"\xE4\xB8\xAD".force_encoding('UTF-8') == "\u4E2D"      =>     true</code></pre>
<p>将Unicode转化为UTF-8字符串：</p>
<pre class="markdown_pre"><code>"\u4E2D".each_byte {|b| print b.to_s(16)} =&gt; 'e4b8ad' #可见Unicode底层用UTF-8字节串存储

#字符串格式
"\u4E2D" # 底层就是UTF-8串

#双字节数字格式
0x4E2D（16进制） 或者 20013（10进制） =&gt; [0x4E2D].pack('U*') =&gt; "\u4E2D"</code></pre>
<p>&nbsp;</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2012-09-25 -- <a target="_blank" href="http://blog.11034.org/2012-09/xiaoao_jianghu.html" title="笑傲江湖">笑傲江湖</a></li><li>2016-06-13 -- <a target="_blank" href="http://blog.11034.org/2016-06/reentrantlock.html" title="可重入锁、ReentrantLock、AQS、Condition">可重入锁、ReentrantLock、AQS、Condition</a></li><li>2013-06-21 -- <a target="_blank" href="http://blog.11034.org/2013-06/cc98_pal5q.html" title="若说结局难改变，我为你绝笔阑珊《仙五前传》人物感言">若说结局难改变，我为你绝笔阑珊《仙五前传》人物感言</a></li><li>2013-06-23 -- <a target="_blank" href="http://blog.11034.org/2013-06/shida.html" title="院版十大了，那就XYT一下~">院版十大了，那就XYT一下~</a></li><li>2017-10-29 -- <a target="_blank" href="http://blog.11034.org/2017-10/guangzhou_macau.html" title="2017国庆超级假期广州澳门游">2017国庆超级假期广州澳门游</a></li></ul><h4 class="related_post_title">看看 ruby on rails </h4><ul class="related_post"><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2015-04-09 -- <a target="_blank" href="http://blog.11034.org/2015-04/json_ascii.html" title="JSON中出现解析错误的原因之一：不可打印字符">JSON中出现解析错误的原因之一：不可打印字符</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-10-04 -- <a target="_blank" href="http://blog.11034.org/2014-10/rails-has_many.html" title="Rails中has_many等的原理">Rails中has_many等的原理</a></li><li>2014-07-28 -- <a target="_blank" href="http://blog.11034.org/2014-07/active_support_235.html" title="简析Rails：active_support 2.3.5">简析Rails：active_support 2.3.5</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2014-12/ruby_encoding.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>linux的timeout处理wkhtmltopdf进程超时</title>
		<link>http://blog.11034.org/2014-10/linux_timeout.html</link>
		<comments>http://blog.11034.org/2014-10/linux_timeout.html#comments</comments>
		<pubDate>Sat, 25 Oct 2014 14:16:17 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Life in Coding]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[timeout]]></category>
		<category><![CDATA[wkhtmltopdf]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2313</guid>
		<description><![CDATA[rails程序中要调用一个系统命令，但是这个系统命令在某些时候可能会处理很久也得不到结果，就需要控制超时情况。 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>rails程序中要调用一个系统命令，但是这个系统命令在某些时候可能会处理很久也得不到结果，就需要控制超时情况。</p>
<p><span id="more-2313"></span></p>
<p>一开始用ruby的<code class="markdown_inline_code">Timeout.timeout</code>方法，首先这方法不是很好用，然后这个方法会开启一个新的线程处理block中的代码，然后当发生超时情况时，即抛出了Timeout::Error异常，是可以处理超时情况，但是那个被开启的线程无法管理，甚至会影响到rails的终端（不能正常Ctrl-C结束了），并且调用系统命令所开启的新进程并不会被中止仍然是继续在后台执行。</p>
<p>听了quark大牛的方法，尽量不要使用ruby的timeout方法，使用靠谱又简单的linux timeout命令。</p>
<pre class="markdown_pre"><code>timeout -k 1 10s cmd 即可

不指定-s，就默认超时后向进程发送TERM信号，即普通的kill

-k 1是指，当10s之后向超时进程发出TERM信号后，在又1秒之后向超时进程发出KILL信号</code></pre>
<p>在ruby中，用ret = `timeout`就可以直接调用系统命令，然后根据ret返回值进行判断是否超时。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;wkhtmltopdf的分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>PS：要用timeout处理的命令是wkhtmltopdf，将HTML页面转化为PDF的开源神器，但是在国内的环境中遇到HTML页面中有被墙了的页面的话，就是执行很久的时间（和浏览器一样），为了防止服务器处于假死状态，需要控制这个执行的时间。</p>
<p>然后顺便说下使用wkhtmltopdf过程中遇到的坑</p>
<ul>
<li>转化HTML中的图片，如果不成功的话，试着尝试使用<code class="markdown_inline_code">xvfb-run --server-args="-screen 0, 1280x1024x24" wkhtmltopdf</code></li>
<li>若转化过程中中文出了问题，检查linux中的微软雅黑（simsun.ttc）、宋体等字体文件是否存在<code class="markdown_inline_code">/usr/share/fonts/truetype</code>中</li>
<li>转化的HTML中必须要把那种全屏遮蔽的div、悬浮窗之类的删掉，大概因为PDF渲染颜色的原因，不仅转化需要超长时间而且打开PDF卡的要死</li>
</ul>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2013-07-13 -- <a target="_blank" href="http://blog.11034.org/2013-07/pal5q_dlc.html" title="仙五前DLC“梦华幻斗”">仙五前DLC“梦华幻斗”</a></li><li>2010-04-30 -- <a target="_blank" href="http://blog.11034.org/2010-04/highschool.html" title="回高中">回高中</a></li><li>2010-09-17 -- <a target="_blank" href="http://blog.11034.org/2010-09/22th_birthday.html" title="22岁的生日">22岁的生日</a></li><li>2015-04-09 -- <a target="_blank" href="http://blog.11034.org/2015-04/json_ascii.html" title="JSON中出现解析错误的原因之一：不可打印字符">JSON中出现解析错误的原因之一：不可打印字符</a></li><li>2017-10-29 -- <a target="_blank" href="http://blog.11034.org/2017-10/macau.html" title="澳门走走">澳门走走</a></li></ul><h4 class="related_post_title">看看 Life in Coding , ruby on rails </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/jabber_and_xmpp.html" title="jabber和XMPP简述原理">jabber和XMPP简述原理</a></li><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2015-04-09 -- <a target="_blank" href="http://blog.11034.org/2015-04/json_ascii.html" title="JSON中出现解析错误的原因之一：不可打印字符">JSON中出现解析错误的原因之一：不可打印字符</a></li><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li><li>2014-12-21 -- <a target="_blank" href="http://blog.11034.org/2014-12/ruby_encoding.html" title="Ruby字符串的编码">Ruby字符串的编码</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2014-10/linux_timeout.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Rails中has_many等的原理</title>
		<link>http://blog.11034.org/2014-10/rails-has_many.html</link>
		<comments>http://blog.11034.org/2014-10/rails-has_many.html#comments</comments>
		<pubDate>Sat, 04 Oct 2014 15:42:56 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[ruby on rails]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2308</guid>
		<description><![CDATA[刚接触rails的时候着实被has_many给惊到，这么厉害的功能，尤其是has_many的延迟加载，直到真正 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>刚接触rails的时候着实被has_many给惊到，这么厉害的功能，尤其是has_many的延迟加载，直到真正需要读取数据的时候才会进行SQL查询（person.posts.size，并不会执行select *到内存后再对Array执行size），但是用普通的调试方法怎么看has_many的结果是一个Array，比如.class方法，然后就觉得很神奇。</p>
<p><span id="more-2308"></span></p>
<p>看了源码之后，就了解has_many实现机制其实很简单，就是由一个代理类<code class="markdown_inline_code">ActiveRecord::Associations::HasManyAssociation</code>来实现的，真正的数据被封装在内部，真正需要用到数据的时候再去加载，这就是延迟加载；至于为什么看上去这么像Array或者说简直就是个Array是因为，<code class="markdown_inline_code">HasManyAssociation</code>算是一个白板类，删掉了从Object继承过来的大部分方法，包括.class方法，当你调用.class方法时就进入到了method_missing方法，然后就交给这个类内部的真正的数据结构Array去调用这个方法，自然表现出来就是一个Array了。所以要显示出这个类原来样貌，只要随意调用一个不存在的函数，比如person.posts.abcdefg()，就会提示HasManyAssociation没有这个方法了。</p>
<p>用法举例：</p>
<pre class="markdown_pre"><code>user.posts     // select * from

user.posts.size 或 user.posts.count  //select count(*) from

user.posts.length // select * from 后，再对Array取size；这个适用于需要先判断数量然后同时取数据（少量），省去count(*)这一条sql

user.posts &lt;&lt; Post.new 或 user.posts.create // insert into ...

user.posts.clear 或 user.posts.destroy_all  // delete from ...</code></pre>
<p><span style="color: #ff6600;"><strong>要尤其注意target的用法</strong></span>，因为<code class="markdown_inline_code">HasOneAssociation</code>本身就有target方法（用来获取Association对象本身），所以若对象本身又有target方法，则首先会调用Association对象的target方法。</p>
<pre class="markdown_pre"><code>class A
    has_one :relation, :class_name =&gt; 'B'
end

class B
    belong_to :target, :class_name =&gt; 'C'
end

a = A.find_by_id(1111)

a.relation =&gt; B的Association对象
p a.relation  =&gt;    B（先调用Association.method_missing方法，转而调用B.inspect方法）

a.relation.target =&gt; 只是B对象
p a.relation.target     =&gt;    B（直接调用B.inspect方法）

a.relation.target.target  =&gt;  C的Association对象
p a.relation.target.target       =&gt;    C</code></pre>
<p>这里选择打印调试则会出现比较奇怪的现象（p a.relation和p a.relation.target的结果是一样的，是因为p和puts调试，调用inpect和to_s方法，都是Association没有的，会通过method_missing转为调用对象本身的方法）</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2013-10-17 -- <a target="_blank" href="http://blog.11034.org/2013-10/google.html" title="Google校招">Google校招</a></li><li>2010-11-14 -- <a target="_blank" href="http://blog.11034.org/2010-11/%e5%8c%97%e6%b5%b7_%e6%99%af%e5%b1%b1_%e5%a4%a9%e5%9d%9b.html" title="北海公园、景山公园、天坛游">北海公园、景山公园、天坛游</a></li><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/jabber_and_xmpp.html" title="jabber和XMPP简述原理">jabber和XMPP简述原理</a></li><li>2016-03-22 -- <a target="_blank" href="http://blog.11034.org/2016-03/spring_mvc_applicationcontext.html" title="Spring-MVC中的一些问题">Spring-MVC中的一些问题</a></li><li>2017-05-12 -- <a target="_blank" href="http://blog.11034.org/2017-05/northwest.html" title="5月青甘大环线6日拼车游">5月青甘大环线6日拼车游</a></li></ul><h4 class="related_post_title">看看 ruby on rails </h4><ul class="related_post"><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2015-04-09 -- <a target="_blank" href="http://blog.11034.org/2015-04/json_ascii.html" title="JSON中出现解析错误的原因之一：不可打印字符">JSON中出现解析错误的原因之一：不可打印字符</a></li><li>2014-12-21 -- <a target="_blank" href="http://blog.11034.org/2014-12/ruby_encoding.html" title="Ruby字符串的编码">Ruby字符串的编码</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-07-28 -- <a target="_blank" href="http://blog.11034.org/2014-07/active_support_235.html" title="简析Rails：active_support 2.3.5">简析Rails：active_support 2.3.5</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2014-10/rails-has_many.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>记C#和Excel开发</title>
		<link>http://blog.11034.org/2014-09/dotnet_csharp_excel.html</link>
		<comments>http://blog.11034.org/2014-09/dotnet_csharp_excel.html#comments</comments>
		<pubDate>Wed, 10 Sep 2014 07:31:54 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Life in Coding]]></category>
		<category><![CDATA[.Net]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2287</guid>
		<description><![CDATA[需要一个windows环境下的工具，能够读写Excel。这个Excel比较复杂，内嵌VB脚本，写Excel的时 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>需要一个windows环境下的工具，能够读写Excel。这个Excel比较复杂，内嵌VB脚本，写Excel的时候必须得保留这些原有的脚本数据。</p>
<p><span id="more-2287"></span></p>
<p>介绍下这个VB脚本，就是给每一行数据提供下拉框进行一些备用数据的选择，比如打标签。这个下拉框并不是Excel提供的控件（控件绑定于具体的Cell或者坐标很麻烦），而是用弹窗实现的，全局共享一个弹窗数据源。这是一段在Excel文件中的VB代码。</p>
<p>最初开始用Python，靠xlrd和xlwt模块进行读写，读的确没什么问题也挺方便，但是写就有点纠结了，xlwt不能写已经存在的excel文件而是需要新创建一个文件然后开始写入然后替换原有文件，如果Excel文件中有设置好的一些格式、控件、脚本的话，就都没了。然后Python最麻烦的当然就是windows下是没有预装Python的，这个工具给别人使用还要带一个Python环境真是麻烦，然后文件就很多包括.py、双击运行的.bat等。</p>
<p>然后想到的就是能够在win环境下良好支持的C#，本科的时候还是学过C#的有那么一点点基础而且毕竟和Java比较像。C#提供的操纵Excel功能的确比较强大，但是没这么好用，很容易报异常需要耐心调试。但是能够编译成.exe，在win环境下运行起来到底方便。</p>
<pre class="markdown_pre"><code>VS创建项目时，会提示选择.Net Framework版本，尽量选择4.0（win7自带）或者更低的，以更好的跨平台；一开始用了默认的4.5（大概是安装VS时候给自动装上的，编译出来在某些win7环境中会报错）

md5的方法：最简单的是用Web模块里的那个方法，但是这个Web模块似乎不太跨平台；网上方法很多，但是算出来的答案居然都不太一样！选个靠谱的不容易...</code></pre>
<p>写代码的时候遇到过的几个坑</p>
<pre class="markdown_pre"><code>xlsApp.Visible = false; （不然每次执行都会打开Excel再关闭）

xlsApp.DisplayAlerts = false; （不然保存的时候会各种提示确认比较烦）

filePath = filePath.Replace('/', '\\');   xlsBook.SaveAs ...（文件路径用 / 就会报异常）

xlsBook.Close(false, Type.Missing, Type.Missing); （没有这句话，程序执行完后Excel的进程就会留在内存中...执行多次就一堆死进程...）</code></pre>
<p>微软的代码风格也就算了，操纵Excel的各种API的命令和使用方法，真是无法直视</p>
<h4  class="related_post_title">看看 .Net , C++ , Excel , Python</h4><ul class="related_post"><li>2014-07-07 -- <a target="_blank" href="http://blog.11034.org/2014-07/ruby_on_rails.html" title="ruby on rails">ruby on rails</a></li><li>2011-10-20 -- <a target="_blank" href="http://blog.11034.org/2011-10/zoj_100_ac.html" title="ZOJ起步 100AC">ZOJ起步 100AC</a></li></ul><h4 class="related_post_title">看看 Life in Coding </h4><ul class="related_post"><li>2016-09-09 -- <a target="_blank" href="http://blog.11034.org/2016-09/jabber_and_xmpp.html" title="jabber和XMPP简述原理">jabber和XMPP简述原理</a></li><li>2015-01-07 -- <a target="_blank" href="http://blog.11034.org/2015-01/prevent_db_duplicate.html" title="防止数据库数据重复的几种方法">防止数据库数据重复的几种方法</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2011-06-11 -- <a target="_blank" href="http://blog.11034.org/2011-06/google_doodle_guitar.html" title="2011-6-9 Google Doodle之电子琴">2011-6-9 Google Doodle之电子琴</a></li><li>2011-03-10 -- <a target="_blank" href="http://blog.11034.org/2011-03/learning_cs.html" title="学计算机的尼玛伤不起啊！！！！！！">学计算机的尼玛伤不起啊！！！！！！</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2014-09/dotnet_csharp_excel.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>简析Rails：active_support 2.3.5</title>
		<link>http://blog.11034.org/2014-07/active_support_235.html</link>
		<comments>http://blog.11034.org/2014-07/active_support_235.html#comments</comments>
		<pubDate>Mon, 28 Jul 2014 13:02:26 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[ruby on rails]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=2272</guid>
		<description><![CDATA[项目中用到Rails2.3.5，就以此版本研究。和Rails本身相关的，大概有actionmailer（发邮件 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>项目中用到Rails2.3.5，就以此版本研究。和Rails本身相关的，大概有actionmailer（发邮件相关）、actionpack（HTTP和controller相关）、activerecord（ORM框架）、activeresource、activesupport（基础扩展库）。</p>
<p><span id="more-2272"></span></p>
<p>activesupport库算是一个基础扩展API库，主要分为两部分，一部分是可供选择的OOP形式的类和API，还有一部分是以Module形式存在的API以打开类的方式直接扩展进入Ruby源代码（即原有基础类）中。打开类扩展的API有一些应该被纳入Rurby源码，写惯Rails代码的脱离了activesupport再去写Ruby估计很不适应。</p>
<p>在po主本机上，库位于：`xx\Ruby193\lib\ruby\gems\1.9.1\gems\activesupport-2.3.5\lib`</p>
<h3>activesupport库的结构</h3>
<ul>
<li>activesupport.rb        已被废弃，require此文件会转require active_support.rb文件，并产生一个warning</li>
<li>active_support.rb      用来导入activesupport库中所有类和模块</li>
<li>active_support目录
<ul>
<li>core_ext目录                    基础扩展库
<ul>
<li>具体模块目录</li>
<li>模块目录导入文件</li>
<li>简单独立模块.rb</li>
</ul>
</li>
<li>vendor目录
<ul>
<li>具体模块目录</li>
</ul>
</li>
<li>具体功能目录&#8230;（如json）</li>
<li>功能导入文件.rb（如json.rb），用以导入同名具体功能目录下所有的文件</li>
<li>其他功能独立文件.rb（如base64.rb、ordered_hash.rb）</li>
</ul>
</li>
</ul>
<h3>activesupport导入流程</h3>
<ol>
<li>require active_support.rb</li>
<li>用autoload方法导入大部分独立类库和目录类库导入文件
<ol>
<li>目录类库导入文件，导入相应的目录下的文件，并作一些初始化工作</li>
</ol>
</li>
<li>require active_support/{vendor, core_ext, dependencies, json}
<ol>
<li>导入core_ext下所有rb文件，当rb文件为导入文件时，再导入core_ext下的目录文件</li>
<li>使用gem安装导入vender下的一些类库（builder、memcache-client、tzinfo、i18n）</li>
</ol>
</li>
</ol>
<p>active_support库的分层加载机制值得模仿学习，每一个子目录都有对应的一个导入rb文件。</p>
<h3>activesupport的core_ext基础库扩展</h3>
<p>按照功能类别排序划分。下面每个子目录下大概有2-10几个rb文件，都是对同一个模块或者类进行扩展，按照功能和意义的不同划分不同的类管理API。</p>
<ul>
<li>class目录</li>
<li>object目录</li>
<li>module目录</li>
<li>kernel目录</li>
</ul>
<ul>
<li>integer目录</li>
<li>numeric目录</li>
<li>bigdecimal目录</li>
<li>float目录</li>
</ul>
<ul>
<li>array目录</li>
<li>enumerable.rb</li>
<li>hash目录</li>
<li>range目录</li>
<li>string目录</li>
<li>proc.rb</li>
<li>symbol.rb</li>
</ul>
<ul>
<li>date目录</li>
<li>time目录</li>
<li>datetime目录</li>
</ul>
<ul>
<li>base64目录</li>
<li>file目录</li>
<li>cgi目录</li>
<li>process目录</li>
<li>pathname目录</li>
</ul>
<h4>Class、Object、Module、Kernel</h4>
<p>对Class的扩展：</p>
<ul>
<li>cattr_accessor :xxx，用来定义@@类变量，同时定义类方法和实例方法</li>
<li>read_inheritable_attribute、write_inheritable_attribute：用名为@inheritable_attributes的实例变量保存了一个hash，用来存储</li>
<li>class_inheritable_accessor :xxx，用类实例变量存储hash，实现同上</li>
<li>subclasses、remove_subclasses、remove_class</li>
</ul>
<p>对Object的扩展：</p>
<ul>
<li><code class="markdown_inline_code">blank?和present?</code></li>
<li>acts_like?(duck)：与具体模块中acts_like_duck?结合使用</li>
<li>with_options：调用可以传入hash键值的函数时，统一加入指定的键值对</li>
<li>tap：在调用链中用来遍历，本身什么也不干，一般用来输出调试</li>
<li>instance_values：将实例变量设置成hash返回</li>
<li>instance_variable_defined?：是否定义了某个实例变量</li>
<li>subclasses_of、extended_by、extend_with_included_modules_from：继承相关的API</li>
</ul>
<p>对Module的扩展：</p>
<ul>
<li>attr_internal :xxx和attr_internal_accessor :xxx，基本和attr_accessor一致，以@_xxx命名格式存储，不容易引起冲突（尤其在继承中）</li>
<li>mattr_accessor :xxx，定义@@xxx的读写方法。和Class的cattr_accessor的区别是，mattr用在module中，cattr用在Class中，混用可能导致错误</li>
<li>alias_method_chain(target, feature)：定义target_without_feature和target_with_feature函数，并将target alias 为feature。此函数在Rails源码中被用到很多</li>
<li>alias_attribute(new_name, old_name)：复制一套属性访问方法<strong><br />
</strong></li>
<li>included_in_classes：被included的Class集合</li>
</ul>
<p>对Kernel的扩展：</p>
<ul>
<li>daemonize：调用Process.daemon</li>
</ul>
<h4>Array、Enumerable、Hash、Range、String</h4>
<p>对Array的扩展：</p>
<ul>
<li>访问元素用的API（access.rb）：from(pos)、to(pos)、second、third、forty_two（这个API有点意思）</li>
<li>格式化输出的API（conversions.rb）：to_param、to_query、to_formatted_s代替to_s、to_xml</li>
<li>extract_options!（extract_options.rb）：取出函数参数中位于最后的hash</li>
<li>将元素分组的API（grouping.rb）：<code class="markdown_inline_code">in_groups_of(num)</code>每个组有num个元素、<code class="markdown_inline_code">in_groups(num)</code>分成num个组、split(value)按元素分组</li>
<li>rand(random_access.rb)：随机访问元素</li>
<li>wrap(wrapper.rb)：将其他对象包装成Array</li>
</ul>
<p>对Enumerable的扩展：</p>
<ul>
<li><code class="markdown_inline_code">index_by</code>：用元素的某个属性建立hash，最常见就是用id作为键</li>
<li><code class="markdown_inline_code">group_by</code>：用元素的某个属性建立元素为array的hash</li>
<li><code class="markdown_inline_code">each_with_object</code>：Ruby1.9中自带的方法</li>
<li>none?</li>
<li>sum?</li>
<li>many?</li>
</ul>
<p>from、to、index_by、group_by这几个函数太常用了，一开始不知道还自己写了一套通用的实现。</p>
<p>对Hash的扩展：</p>
<ul>
<li>格式化输入和输出的API(conversions.rb)：Hash.from_xml、to_xml、to_query</li>
<li>deep_merge和deep_merge!(deep_merge.rb)：当同一key两value都是Hash时递归式merge键值对</li>
<li>diff（diff.rb）：两个hash之间不同的键值对</li>
<li>except和except!（except.rb）：去除指定的key</li>
<li><code class="markdown_inline_code">slice</code>和slice!（slice.rb）：只留下指定的key</li>
<li>with_indifferent_access方法和HashWithIndifferentAccess类（indifferent_access.rb）：字符串和符号键无区别对待</li>
<li>reverse_merge和reverse_merge!（reverse_merge.rb）：反方向merge</li>
<li>格式化key的API（keys.rb）：stringify_keys、stringify_keys!和symbolize_keys、symbolize_keys!，前两个将所有key转化为字符串形式，后两个转为为符号</li>
</ul>
<p>对String的扩展：</p>
<p>文件太多，就列举一下API。</p>
<ul>
<li>访问类API：at(pos)用来解决Ruby1.8中返回int值的结构、<code class="markdown_inline_code">from(pos)、to(pos)</code>、first、last</li>
<li>转化为时间类API：<code class="markdown_inline_code">to_time(form = :utc)、to_date、to_datetime</code></li>
<li>squish和squish!：去掉字符串前后空格，并将内部的连续空格合并为1个</li>
<li>格式化字符串类API：使用外部的Inflector类实现
<ul>
<li>pluralize：复数形式</li>
<li>singularize：单数形式</li>
<li>camelize：驼峰形式</li>
<li>underscore：下划线形式</li>
<li>dasherize：中划线形式</li>
<li>demodulize：去除模块名之前的前导模块，得到最后的模块名</li>
<li>titleize：首字母大写</li>
<li>humanize：最佳阅读形式</li>
<li>classify：类名形式</li>
</ul>
</li>
<li>each_char：遍历每个字符</li>
<li>Ruby1.8字节串到字符串的转化API：<code class="markdown_inline_code">mb_chars、is_utf8?</code></li>
<li><code class="markdown_inline_code">starts_with?和end_with?</code></li>
<li>升级+和&lt;&lt;、add_with_safety、html_safe?：将+和&lt;&lt;升级用add_with_safety实现</li>
</ul>
<p>对Range的扩展：</p>
<ul>
<li>include?扩展即include_with_range?（include_range.rb）：升级为同时支持Range对象的include?判断</li>
<li>overlaps?（overlaps.rb）：判断是否覆盖</li>
</ul>
<h4>Proc和Symbol</h4>
<p>对Proc的扩展：只有一个bind函数，讲Proc绑定到一个object作为一个实例方法</p>
<p>对Symbol的扩展：只有一个<code class="markdown_inline_code">to_proc</code>函数（即对&amp;操作符的处理），配合起来index_by、group_by方法不要太给力。</p>
<p>hash_by_id = list.index_by(&amp;:id)            hash_group_by_type = list.group_by(&amp;:type)</p>
<h4>Integer、Numeric、Float</h4>
<p>很大程度配合Date、Time、DateTime模块。</p>
<p>对Integer的扩展：</p>
<ul>
<li>even?、odd?</li>
<li>ordinalize：给数字末尾加上序号串，1=&gt;1st，102=&gt;102nd</li>
<li>months、years：配合日期的加减或者直接打印，返回值是Duration对象</li>
</ul>
<p>对Numeric的扩展：</p>
<ul>
<li>bytes、kilobytes、megabytes、gigabytes、terabytes、petabytes、exabytes和所有的单数形式，节省计算各个1024</li>
<li>seconds、minutes、hours、days、weeks、fortnights：同Integer的months</li>
<li>ago(time)和untile(time)、since(time)和from_now(time)：前者time &#8211; self，后者time + self</li>
</ul>
<p>对Float的扩展：</p>
<ul>
<li>round_with_precision(precision = nil)：按照精度四舍五入</li>
<li>years、months</li>
</ul>
<h4>Date、Time和DateTime</h4>
<p>要结合core_ext同目录下的duration.rb中的Duration类实现。给这三个类增加的API都比较类似，就大致举例一些API和用法好了。</p>
<p>以下方法基本为Date和Time类共享：</p>
<ul>
<li>升级+和-为可以计算Duration类型的方法plus_with_duration(other)、minus_with_duration(other)</li>
<li>yesterday、tomorrow</li>
<li>past?、today?、future?</li>
<li>beginning_of_day、midnight、at_midnight、end_of_day、beginning_of_week、end_of_week、beginning_of_month、end_of_month、beginning_of_quarter、end_of_quarter、beginning_of_year、end_of_year</li>
<li>months_ago、months_since、years_ago、years_since</li>
<li>last_year、next_year、last_month、next_month、next_week</li>
<li>to_s(:db)：date对象直接生成%Y-%m-d形式，time对象生成%Y-%m-%d %H:%M:%S</li>
</ul>
<p>对Time加上时区的扩展：都是静态函数</p>
<ul>
<li>zone的读写</li>
<li>zone_default的读写</li>
<li>use_zone(time_zone)：修改时区</li>
<li>current</li>
</ul>
<h4>其他</h4>
<h3>activesupport的vendor</h3>
<p>现在只有builder、i18n、tzinfo、memcache-client四个模块</p>
<h3>activesupport的可选功能库</h3>
<p>按照个人觉得有用度排序，有些感觉没啥用的和暂时看不懂得没有列出来</p>
<ul>
<li>ordered_hash.rb和ordered_options.rb：保持插入顺序的Hash；OrderedOptions可以用hash.xxx = &#8216;xxx&#8217;的方式存储键值对（method_missing实现）</li>
<li>duration.rb：实现1.month.ago这样的时间跨度，用于对基础库Date和Time的扩展</li>
<li>callbacks.rb：define_callbacks :before_save, :after_save这样的钩子方法</li>
<li>rescuable.rb：捕获所有指定类型的异常，做统一处理</li>
<li>time_with_zone.rb：对Time类扩展时区功能</li>
<li>base64.rb：加密、解密</li>
<li>gzip.rb：用gzip算法压缩和读取string</li>
<li>json目录</li>
<li>multibyte目录：对Ruby1.8支持Unicode</li>
<li>cache目录：定了好几种存储方式，
<ul>
<li>drb_store</li>
<li>file_store</li>
<li>mem_cache_store</li>
<li>memory_store，就是个Hash</li>
<li>synchronized_memory_store，同步的memory_store</li>
<li>compressed_mem_cache_store</li>
</ul>
</li>
<li>locale目录</li>
<li>testing目录</li>
<li>values目录</li>
<li>xml_mini目录</li>
</ul>
<h3>其他学习到的地方</h3>
<ul>
<li>Ruby1.9+和Ruby1.8差别非常大，导致active_support代码中要各种判断Ruby版本
<ul>
<li>字符串用Unicode实现</li>
<li>Hash用OrderedHash实现</li>
<li>加入了BaseObject作为白板类</li>
</ul>
</li>
<li>Object的act_like?(xxx)方法和其他模块分别定义act_like_xxx?方法，配合实现类似Java的接口机制，一般单独写在behavior.rb文件的Behavior模块中</li>
</ul>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2010-09-25 -- <a target="_blank" href="http://blog.11034.org/2010-09/graduate_interview.html" title="免研面试">免研面试</a></li><li>2010-05-09 -- <a target="_blank" href="http://blog.11034.org/2010-05/%e7%bb%8d%e5%85%b4%e6%b8%b8.html" title="母亲节，绍兴游">母亲节，绍兴游</a></li><li>2011-04-18 -- <a target="_blank" href="http://blog.11034.org/2011-04/sjtu.html" title="擦肩而过的上海交大">擦肩而过的上海交大</a></li><li>2010-08-10 -- <a target="_blank" href="http://blog.11034.org/2010-08/first_day_overtime.html" title="第一天加班">第一天加班</a></li><li>2015-04-07 -- <a target="_blank" href="http://blog.11034.org/2015-04/wuhan.html" title="美丽樱花，“文明”武汉">美丽樱花，“文明”武汉</a></li></ul><h4 class="related_post_title">看看 ruby on rails </h4><ul class="related_post"><li>2015-08-12 -- <a target="_blank" href="http://blog.11034.org/2015-08/rails_nginx.html" title="Liunx下搭建Rails和Nginx环境">Liunx下搭建Rails和Nginx环境</a></li><li>2015-04-09 -- <a target="_blank" href="http://blog.11034.org/2015-04/json_ascii.html" title="JSON中出现解析错误的原因之一：不可打印字符">JSON中出现解析错误的原因之一：不可打印字符</a></li><li>2014-12-21 -- <a target="_blank" href="http://blog.11034.org/2014-12/ruby_encoding.html" title="Ruby字符串的编码">Ruby字符串的编码</a></li><li>2014-10-25 -- <a target="_blank" href="http://blog.11034.org/2014-10/linux_timeout.html" title="linux的timeout处理wkhtmltopdf进程超时">linux的timeout处理wkhtmltopdf进程超时</a></li><li>2014-10-04 -- <a target="_blank" href="http://blog.11034.org/2014-10/rails-has_many.html" title="Rails中has_many等的原理">Rails中has_many等的原理</a></li>]]></content:encoded>
			<wfw:commentRss>http://blog.11034.org/2014-07/active_support_235.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
