<?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; Java</title>
	<atom:link href="http://blog.11034.org/category/coding/java/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>读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>2011-07-21 -- <a target="_blank" href="http://blog.11034.org/2011-07/hk_90_movies.html" title="回味香港90年代电影，大爱王祖贤">回味香港90年代电影，大爱王祖贤</a></li><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</a></li><li>2013-03-13 -- <a target="_blank" href="http://blog.11034.org/2013-03/journey_to_the_west.html" title="大话西游，西游降魔">大话西游，西游降魔</a></li><li>2011-03-07 -- <a target="_blank" href="http://blog.11034.org/2011-03/zijingang_to_shanghai.html" title="玉泉、紫金港、上海">玉泉、紫金港、上海</a></li><li>2013-01-08 -- <a target="_blank" href="http://blog.11034.org/2013-01/jiuzhai_2.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>2016-06-08 -- <a target="_blank" href="http://blog.11034.org/2016-06/tomcat_shutdown.html" title="Tomcat监听shutdown释放数据库连接池">Tomcat监听shutdown释放数据库连接池</a></li><li>2014-07-07 -- <a target="_blank" href="http://blog.11034.org/2014-07/jay.html" title="杰伦，祝你幸福！">杰伦，祝你幸福！</a></li><li>2011-07-29 -- <a target="_blank" href="http://blog.11034.org/2011-07/baidu_mobads_online.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-11-25 -- <a target="_blank" href="http://blog.11034.org/2012-11/groups_of_orz.html" title="[合集]玩只言片语的那些2货啊&#8230;">[合集]玩只言片语的那些2货啊&#8230;</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-12-06 -- <a target="_blank" href="http://blog.11034.org/2012-12/suffix_array.html" title="一套可用的后缀数组代码">一套可用的后缀数组代码</a></li><li>2011-10-14 -- <a target="_blank" href="http://blog.11034.org/2011-10/google_2012_campus.html" title="Google2012校招笔试">Google2012校招笔试</a></li><li>2010-09-14 -- <a target="_blank" href="http://blog.11034.org/2010-09/wp-postviews_do_with_page_refresh.html" title="wp-postviews的防刷新的处理">wp-postviews的防刷新的处理</a></li><li>2016-03-03 -- <a target="_blank" href="http://blog.11034.org/2016-03/xiamen.html" title="春节再游厦门">春节再游厦门</a></li><li>2025-05-19 -- <a target="_blank" href="http://blog.11034.org/2025-05/singapure_kul.html" title="25五一新马游">25五一新马游</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>2015-04-07 -- <a target="_blank" href="http://blog.11034.org/2015-04/wuhan.html" title="美丽樱花，“文明”武汉">美丽樱花，“文明”武汉</a></li><li>2025-05-07 -- <a target="_blank" href="http://blog.11034.org/2025-05/sanya_sg_my.html" title="25年三亚&#038;新马游">25年三亚&#038;新马游</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-12-09 -- <a target="_blank" href="http://blog.11034.org/2015-12/snow_spring_seafood.html" title="大雪、温泉、海鲜の北海道">大雪、温泉、海鲜の北海道</a></li><li>2011-10-14 -- <a target="_blank" href="http://blog.11034.org/2011-10/google_2012_campus.html" title="Google2012校招笔试">Google2012校招笔试</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>2013-02-06 -- <a target="_blank" href="http://blog.11034.org/2013-02/pal5q.html" title="仙五前传，牵绊">仙五前传，牵绊</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>2010-05-15 -- <a target="_blank" href="http://blog.11034.org/2010-05/interview_of_ebay.html" title="eBay实习笔试&#038;面试">eBay实习笔试&#038;面试</a></li><li>2010-11-30 -- <a target="_blank" href="http://blog.11034.org/2010-11/two_wordpress_plugins_and_some_tips.html" title="推荐2个wordpress插件和几个tips">推荐2个wordpress插件和几个tips</a></li><li>2010-12-27 -- <a target="_blank" href="http://blog.11034.org/2010-12/scda_beijing.html" title="SCDA北京小聚">SCDA北京小聚</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>2017-10-09 -- <a target="_blank" href="http://blog.11034.org/2017-10/car_card.html" title="沪C转浙A含抵押的流程">沪C转浙A含抵押的流程</a></li><li>2013-11-25 -- <a target="_blank" href="http://blog.11034.org/2013-11/westlake_seasons.html" title="西湖四季">西湖四季</a></li><li>2011-02-17 -- <a target="_blank" href="http://blog.11034.org/2011-02/baidu_shanghai.html" title="百度上研一日游&#038;沪杭高铁初体验">百度上研一日游&#038;沪杭高铁初体验</a></li><li>2010-10-17 -- <a target="_blank" href="http://blog.11034.org/2010-10/beijing_baidu.html" title="北京，百度">北京，百度</a></li><li>2013-01-08 -- <a target="_blank" href="http://blog.11034.org/2013-01/jiuzhai_1.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/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>学到的一些东西</title>
		<link>http://blog.11034.org/2013-08/some_skills_in_java.html</link>
		<comments>http://blog.11034.org/2013-08/some_skills_in_java.html#comments</comments>
		<pubDate>Sat, 10 Aug 2013 03:11:52 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Spring]]></category>

		<guid isPermaLink="false">http://blog.11034.org/?p=1895</guid>
		<description><![CDATA[暑假在天猫实习，从头开始参与一个项目。项目虽小，五脏俱全，虽然自己实现的是一个十分简单如果完全用自己的环境和习 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>暑假在天猫实习，从头开始参与一个项目。项目虽小，五脏俱全，虽然自己实现的是一个十分简单如果完全用自己的环境和习惯写代码不用一天就能完成的任务，结果就是因为搭上了淘宝整个系统和环境，变得十分复杂，而且还要和其他team约定接口联调测试，还要和测试、DBA、前台、PD（产品经理）等打交道。</p>
<p>相比本科的百度上海那时候的实习经历，最大的区别就是那时候完全就用一套开源的简单的东西，能多简单能跑起来就好，几乎没有和百度已有的平台去接通（除了一块不是我负责的登陆系统），打交道的主要就是一位前端，再后来就是一个测试，没有接触太多的人和其他的技术。</p>
<p><span id="more-1895"></span></p>
<p>下面是项目里碰到几个自己当时解决不了，在景升和楚帅的帮忙下（耗费较长时间debug啊）给解决的，挺有意思的。</p>
<h2>Spring加载properties配置文件</h2>

<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;context:property-placeholder</span> <span style="color: #000066;">location</span>=<span style="color: #ff0000;">&quot;/WEB-INF/jdbc.properties&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></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;bean</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;property</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;locations&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;list<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;value<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>classpath:jdbc.properties<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/value<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/list<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/property<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/bean<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></td></tr></table></div>

<p><context:property-placeholder />标签和PropertyPlaceholderConfigurer这个Bean的实现是一个原理，所以如果同时出现在<Beans>标签中，之后出现的那个会被忽略（Bean已存在），导致后一个的配置文件加载失败。所以应该推荐使用PropertyPlaceholderConfigurer的方式加载多个配置文件，并且将这个Bean放在最顶层的Beans配置文件中，供所以开发者使用。</p>
<h2>特殊化/项目化的配置文件名</h2>
<p>由于习惯，Spring的配置文件applicationContext.xml、ibatis的配置文件sql-map.xml等随处可见，当项目庞大之后，很难保证引入的第三方jar包内没有这些同名文件，弄不好因为classpath加载顺序在加载&#8221;classpath:sql-map.xml&#8221;时加载了错误的xml文件而百思不得其解。</p>
<p>反正这些配置文件再加载时，都在可以自定义文件名的，所以给一个namespace是比较好的习惯，比如appname_sql-map.xml。</p>
<h2>追寻jar包</h2>
<p>Maven只能帮助构建项目依赖，具体运行时，还是依赖classpath，服务器和本地机器环境不可能完全相同，越是复杂的环境越容易加载错jar包，导致版本不一致，而导致错误。<br />
可以通过<strong>class.getProtectionDomain().getCodeSource().getLocation()</strong>获得jar包的URL对象，进行日志打印就可以知道此class所属的jar包的具体路径信息，以此判断运行状况是否符合期望。</p>
<h2>Eclipse远程断点调试</h2>
<p>只要服务器和本地的代码完全一致，在Debug模式下的Open Debug Dialog，左侧菜单最后一个Remote Java Application，填写了项目、IP和端口后，设了断点就可以按右下角Debug进行断点。不过对服务器进行断点容易造成服务器崩溃&#8230;小心使用。</p>
<h4  class="related_post_title">看看 Spring</h4><ul class="related_post"><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>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/2013-08/some_skills_in_java.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>java.util中几个Map的性能测试</title>
		<link>http://blog.11034.org/2013-05/java_map.html</link>
		<comments>http://blog.11034.org/2013-05/java_map.html#comments</comments>
		<pubDate>Mon, 27 May 2013 08:47:35 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[数据结构和算法]]></category>
		<category><![CDATA[hash表]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1786</guid>
		<description><![CDATA[突然有这么个想法，就写了段代码测试了下。在单线程下对Java里所有Map的性能测试，包括HashMap、Has [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>突然有这么个想法，就写了段代码测试了下。在单线程下对Java里所有Map的性能测试，包括HashMap、Hashtable、LinkedHashMap、IdentityMap、TreeMap，没有测试WeakHashMap。</p>
<p>数据为&lt;Integer, Integer&gt;，范围在0~1千万，共1百万对数据，由Python的random.randint()自动生成。</p>
<p>测试的性能包括写入（put）、随机读（get）和遍历（foreach语法），也是Map这个数据结构最常用的三个操作。其中写入就是将这1百万对测试数据依次put一遍，随机读就是在上一步写入之后再按顺序get一遍。数据显示最后Map大小都为995012（当然IdentityMap的size是1百万），就是有近5000条重复的key。<br />
<span id="more-1786"></span></p>
<p>下面是笔记本上跑出的数据：</p>
<p align="center">Java中各个Map的性能测试（T400笔记本，酷睿P8700/2.46G）数据表</p>
<div align="center">
<table border="0" cellspacing="1" cellpadding="4" bgcolor="silver">
<tbody>
<tr>
<th bgcolor="#FFFFFF" width="150">
<div align="center">Map</div>
</th>
<th bgcolor="#FFFFFF" width="250">
<div align="center">Run Time(ms)</div>
</th>
<th bgcolor="#FFFFFF" width="150">
<div align="center">Type[order]</div>
</th>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">HashMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">744, 739, 736, 741, 741</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[3]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">220, 225, 228, 225, 221</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[2]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">239, 218, 213, 209, 210</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[1]</div>
</td>
</tr>
<tr>
<td rowspan="3" align="center" valign="top" bgcolor="#FFFFFF">
<div align="center">Hashtable</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">694, 726, 699, 685, 686</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[2]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">264, 272, 272, 266, 264</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[4]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">331, 291, 284, 295, 286</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[2]</div>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">LinkedHashMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">814, 802, 817, 825, 818</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[4]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">227, 229, 218, 223, 227</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[2]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">481, 466, 472, 475, 473</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[4]</div>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">IdentityHashMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">649, 660, 658, 662, 659</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[1]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">181, 186, 188, 191, 182</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[1]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">754, 774, 773, 774, 773</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[5]</div>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">TreeMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">1880, 1879, 1876</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[5]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">1229, 1234, 1270</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[5]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">420, 403, 399, 401, 401</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[3]</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>然后又在实验室的台式电脑上跑了一下测试，基本结果类似。</p>
<p align="center">Java中各个Map的性能测试（台式机，i3/4G）数据表</p>
<div align="center">
<table border="0" cellspacing="1" cellpadding="4" bgcolor="silver">
<tbody>
<tr>
<th bgcolor="#FFFFFF" width="150">
<div align="center">Map</div>
</th>
<th bgcolor="#FFFFFF" width="250">
<div align="center">Run Time(ms)</div>
</th>
<th bgcolor="#FFFFFF" width="150">
<div align="center">Type[order]</div>
</th>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">HashMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">422, 423, 438</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[2]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">234, 218, 218</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[2]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">250, 234, 234</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[1]</div>
</td>
</tr>
<tr>
<td rowspan="3" align="center" valign="top" bgcolor="#FFFFFF">
<div align="center">Hashtable</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">407, 422, 406</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[1]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">249, 250, 250</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[4]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">250, 250, 249</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[1]</div>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">LinkedHashMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">610, 594, 594</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[4]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">234, 234, 234</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[3]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">265, 280, 280</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[3]</div>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">IdentityHashMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">532, 516, 531</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[3]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">172, 172, 172</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[1]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">327, 328, 312</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[4]</div>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" bgcolor="#FFFFFF">
<div align="center">TreeMap</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">1437, 1421, 1431</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Put[5]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">1185, 1185, 1186</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Get[5]</div>
</td>
</tr>
<tr>
<td bgcolor="#FFFFFF">
<div align="center">327, 328, 343</div>
</td>
<td bgcolor="#FFFFFF">
<div align="center">Foreach[4]</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>与上一份数据相比，区别如下：</p>
<ul>
<li>HashMap和Hashtable的操作结果类似，但数据基本很接近，差异不大</li>
<li>IdentityHashMap的写入速度比HashMap慢了一些，读的速度依旧最快，foreach虽然仍然很慢但基本和TreeMap一致，没有太夸张</li>
</ul>
<p>从上面数据上可以看出一些有趣的地方，大部分都很好理解，倒是有2处不太理解呢。</p>
<h3>HashMap</h3>
<p>HashMap不愧为精心设计的最常用的Map，读写性能基本上都是最优秀的，即使某项不是最最优秀的也相差无多，综合性能是毫无争议的最高。</p>
<p>这边也谈谈HashMap源码中对Object.hashCode()再次hash的内部函数：static int hash(int h) {}。之前看源码的时候也只是知道它二次hash的目的以及全部二进制抑或和移位的实现方法，代码原理完全没看懂，而且也没有深究二次hash的原因直到在面试天猫的时候被问到了，倒是傻了一下含糊了说了一些猜测的原因，提高hash分布均匀这样的。</p>
<p>这里附上源码中对此函数的注释，不是太好理解。个人理解：这个函数用来避免质量差的hashCode，因为HashMap的hash表是2的整数倍大小的，普通的hashCode在低bit位的冲突会发生的特别多。</p>
<pre>/**
 * Applies a supplemental hash function to a given hashCode, which
 * defends against poor quality hash functions. This is critical
 * because HashMap uses power-of-two length hash tables, that
 * otherwise encounter collisions for hashCodes that do not differ
 * in lower bits.
 */</pre>
<h3>Hashtable</h3>
<p>相信应该没有多少代码会用到Hashtable了，但不得不惊讶于其单论写入（put）性能竟然优于HashMap，虽然快了没多少基本可以忽略，但比较与读和遍历上要慢上一些，这不能不说很神奇，而且从源码层面一直没怎么看出来原因在哪里。因为synchronized修饰包含占用锁的消耗，速度慢一些是好理解的，当然代码上比HashMap要简单一些调用的子函数也少一些但这不足以使得性能会优于HashMap啊，不得其解，求指导~</p>
<h3>LinkedHashMap</h3>
<p>LinkedHashMap从HashMap继承而来，自然速度会慢一些些。主要重写了内部addEntry()方法（主要写操作put时候被调用）来维护插入顺序链表，所以写入速度比HashMap慢是好理解的；默认内部变量accessOrder == false表示不会在get()方法调用时使用LRU策略修改链表，所以默认随机读的性能和HashMap完全一致；至于foreach遍历，HashMap走的是hash table数组，LinkedHashMap走的是链表，指针操作比数组操作慢了一些。</p>
<h3>IdentityHashMap</h3>
<p>IdentityHashMap和以上几个Map使用的冲突策略不同为线性探测法，所有的冲突操作由邻接链表的指针操作转化为数组操作，所以随机读速度比HashMap快了不少，至于写的话大概差不多（两份测试数据不一致），但是foreach遍历却这么慢有点想不通了源代码也没看出所以然来。</p>
<h3>TreeMap</h3>
<p>这个就不用多说了，时间复杂度都不一样的，实现巨复杂的红黑树，当然速度非常慢了，不过它的foreach遍历效率相对而言是比较高的，因为Tree中邻接节点是有连接关系的，和其他Map相差并不多。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>再把上面提到的2个问题列举下：</p>
<ol>
<li>为什么Hashtable单论写入速度比HashMap快？（<span style="color: #0000ff;">不同机器、JVM、JDK版本会导致运行结果差异，所以问题意义不大</span>）</li>
<li>为什么IdentityHashMap的foreach遍历速度却很慢？</li>
</ol>
<div>发博文后，辛苦串叔拿着测试代码和数据在不同的JVM和不同JDK的版本下进行测试，发现结果并不是太一致，所以纠结这快一点慢一点没有太大的意义，可能就是本身Java代码和JVM实现的问题了，所以不再深究啦，大致了解这么一个情况就可以啦。</div>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2010-10-30 -- <a target="_blank" href="http://blog.11034.org/2010-10/%e9%a6%99%e5%b1%b1%e7%ba%a2%e5%8f%b6.html" title="北京香山红叶">北京香山红叶</a></li><li>2023-09-10 -- <a target="_blank" href="http://blog.11034.org/2023-09/huzhou_sheraton.html" title="湖州喜来登·安吉博物馆">湖州喜来登·安吉博物馆</a></li><li>2011-08-13 -- <a target="_blank" href="http://blog.11034.org/2011-08/lotus_of_west_lake.html" title="曲院风荷，婀娜多姿">曲院风荷，婀娜多姿</a></li><li>2014-09-22 -- <a target="_blank" href="http://blog.11034.org/2014-09/thinkpad_dying.html" title="T400快挂啦">T400快挂啦</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">看看 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/2013-05/java_map.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Tomcat的Connector：Coyote</title>
		<link>http://blog.11034.org/2013-05/coyote.html</link>
		<comments>http://blog.11034.org/2013-05/coyote.html#comments</comments>
		<pubDate>Mon, 20 May 2013 14:01:09 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[coyote]]></category>
		<category><![CDATA[Socket]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[多线程]]></category>
		<category><![CDATA[异步IO]]></category>
		<category><![CDATA[线程池]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1808</guid>
		<description><![CDATA[上一篇博文讲了Tomcat的系统架构，今天花了一天时间研究了下coyote的源码，大致对普通IO版本有所了解。 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>上一篇博文讲了<a title="Tomcat6源码学习" href="/2013-05/tomcat6.html" target="_blank">Tomcat的系统架构</a>，今天花了一天时间研究了下coyote的源码，大致对普通IO版本有所了解。被同学提醒后发现，Tomcat6已经支持异步IO了，但默认是普通IO版本，需要在Connector的protocol参数中定义为Http11NioProtocol才开启异步IO模式。</p>
<p>主要的代码，Socket的服务在org.apache.tomcat.util.net.JIoEndpoint类，解析处理HTTP请求在org.apache.coyote.http11包的Http11Protocol类和Http11Processor类，org.apache.catalina.connector.CoyoteAdapter负责连接Connector模块（coyote）和Container模块。</p>
<p>线程池默认用的JIoEndpoint类中的子类WorkerStack，若在server.xml配置使用了Executor，则使用org.apache.catalina.core.StandardThreadExecutor（对ThreadPoolExecutor的一层包装）。并发量高的情况下Executor的效率会高一些。<span id="more-1808"></span></p>
<h2>Coyote普通IO流程</h2>
<p>在Catalina主线程启动过程中：</p>
<p>StandardService的init()中，在JIoEndpoint.init()中，ServerSocket(port = 8080, backlog = 100)被new出来。</p>
<p>StandardService的start()中，在JIoEndpoint.start()中，一个新的线程（名叫http-8080-Acceptor-0）被start()，这个由JIoEndpoint.Accept内部类执行run()的线程干的活就是不停地从serverSocket.accept()，然后根据线程池的选择拿一个线程去处理socket。因为处理在新的线程，Acceptor线程继续等待accept()。</p>
<p>若使用默认的WorkerStack线程池，JioEndpoint.getWorkerThread().assign(socket);使Worker线程获得socket实例后，就可以在新线程中处理socket。</p>
<p>若使用Executor线程池，则new出一个JioEndpoint.SocketProcessor()交给Executor.execute()，在新线程中处理socket。</p>
<p>不管哪种线程池方法，新线程中使用一样的代码，调用Http11Protocol$Http11ConnectionHandler.process(socket)，这里使用了一个ConcurrentLinkedQueue用来保存Http11Processor实例，拿一个进行处理。</p>
<p>Http11Protocol.process()具体解析HTTP请求。解析过程中，将参数塞进request和response，再调用CoyoteAdapter.service(request, response)，这样就进入Container模块去调用Servlet代码。</p>
<p>结束后，回收Http11Processor实例，回收线程。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;流程结束分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>好像代码也没有太难看不懂，大多数复杂的线程同步操作都由java.util.concurrnt包里的类实现了。</p>
<h2>Worker线程的assign()和await()</h2>
<p>Acceptor线程调用Worker.assign( socket )，将socket对象赋给Worker线程，然后Worker线程中的await()方法通过线程同步，Worker线程被唤醒并拿到了socket就可以在自己线程里顺利执行了。</p>
<p>这块线程同步的代码蛮有意思的，而且Worker线程也借助这边的wait()和notifyall()实现挂起和唤醒。</p>
<h2>server.xml参数调优</h2>
<p><strong>开启异步IO</strong></p>
<p>Connector的配置中，将protocol=“HTTP/1.1”改为“org.apache.coyote.http11.Http11NioProtocol”就可以使用nio包的异步IO，效率高不少。</p>
<p>对java.nio包特别是异步IO这一块了解甚浅，而且Tomcat对这一块的实现略复杂，看懂略有难度，努力！</p>
<p>同样地方还能改为“org.apache.coyote.http11.Http11AprProtocol”，据说利用底层OS的异步IO，需要APR和Native这其他两个组件的支持才能工作，效率更高，不是太确定。</p>
<p><strong>开启Executor并在Connector中使用</strong></p>
<p>Executor的2个配置参数，minSpareThreads和maxThreads，对应到ThreadPoolExecutor(int <strong>corePoolSize</strong>,  int <strong>maximumPoolSize</strong>, &#8230;)这个构造的前面2个参数。分别表示活跃线程数和最大线程数。</p>
<p><strong>Connector的参数：</strong></p>
<ul>
<li>maxThreads，对应JIoEndpoint类中的maxThreads变量，默认为200。若有Executor加入则maxThreads被取代。</li>
<li>acceptCount，对应JIoEndpoint类中的backlog变量，默认为100，用作ServerSocket的第二个参数。ServerSocket里等待队列的长度。</li>
</ul>
<h4  class="related_post_title">看看 coyote , Socket , Tomcat , 多线程 , 异步IO , 线程池</h4><ul class="related_post"><li>2013-05-19 -- <a target="_blank" href="http://blog.11034.org/2013-05/tomcat6.html" title="Tomcat6源码学习">Tomcat6源码学习</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>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-03-01 -- <a target="_blank" href="http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html" title="jdk中SimpleDateFormat的实例线程不安全">jdk中SimpleDateFormat的实例线程不安全</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/2013-05/coyote.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Tomcat6源码学习</title>
		<link>http://blog.11034.org/2013-05/tomcat6.html</link>
		<comments>http://blog.11034.org/2013-05/tomcat6.html#comments</comments>
		<pubDate>Sun, 19 May 2013 10:27:43 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[ClassLoader]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[Servlet]]></category>
		<category><![CDATA[Socket]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[事件驱动]]></category>
		<category><![CDATA[多线程]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[生命周期]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1803</guid>
		<description><![CDATA[在上一篇博文中说到因为电脑风扇坏了2天于是看完了《How tomcat works》译本，不过这本书是针对To [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在<a title="小黑风扇坏两天，被逼看完一本书" href="/2013-05/fan_error.html" target="_blank">上一篇博文</a>中说到因为电脑风扇坏了2天于是看完了《How tomcat works》译本，不过这本书是针对Tomcat4和5来讲的比较老了，然后再结合Tomcat6.0.26的源代码调试学习了总共一周的时间，算是大致将Tomcat6.0的一部分工作机制（JSP、JMX、安全管理等模块没研究）给搞清楚了。大概是因为平时基于Java的Web程序写的太多，对Tomcat这一块源码研究特别感兴趣，有时候为了想通一个问题兴奋地吃饭睡觉都在一直想，非常有动力并且也有效率，好久没有这种感觉了。</p>
<p><span id="more-1803"></span>列几点觉得Tomcat设计上比较有意思的地方。</p>
<h2>Connector模块</h2>
<p>Tomcat主要分为Connector模块和Container模块，前者就是处理Socket和HTTP请求，后者是就是常听到的Servlet容器。</p>
<p>Connector模块由于功能比较独立，然后Tomcat内部接口的设计允许第三方组件的使用，就使用了Coyote组件，虽然也是apache下的。有空可以再好好来学习下Coyote的源代码，因为这块涉及了多线程和线程同步、资源优化、效率优化等方面，很值得学习。</p>
<p>Tomcat默认一共占用了3个端口，8080（HTTP/1.1）、8009（AJP/1.3，接受其他服务器转发的请求）和8005（等待SHUTDOWN命令，由Server类在主线程中开启）。其中8080和8009由Connector模块管理，所以默认有2个Connector在运作。</p>
<h2>Container模块</h2>
<p>由Engine、Host、Context、Wrapper四个容器组合而成，分别表示整个引擎、虚拟主机、Web应用、Servlet，通过父子关系连接，然后配套相应的Pipeline和Valve实现过程流。</p>
<h2>Lifecycle接口</h2>
<p>包括启动类（Catalina）、Server、Service、Connector、Container、Executor、Loader、Manager、Realm、Resources、Pipeline都实现了这个接口，通过简单的start()和stop()来递归实现生命周期的管理。</p>
<p>并且这一模块中还用到了LifecycleListener和LifecycleEvent这套事件驱动模型，虽然觉得Tomcat的事件驱动做的比较一般性，注册在某个Lifecycle的LifecycleListener会收到这个Object的所有事件通知，要靠自己去辨别事件类型并作出相应反应，效率较低且定制性弱。</p>
<h2>ClassLoader的设计</h2>
<p>Tomcat为了使类库的私有性，有commonLoader用来加载lib下的jar包，catalinaLoader和sharedLoader分别用来加载Tomcat类库和应用程序共享类库且都以commonLoader为parent。但是在Tomcat6的配置中，默认没有开启catalinaLoader和sharedLoader也没有相应的server和shared目录，导致这3个loader就是同一个commonLoader。这3个loader是StandardClassLoader的实例。</p>
<p>然后对于每一个Web应用程序，都有自己的一个WebappClassLoader，其parent为sharedLoader。这样可以防止不同Web应用程序之间加载互相类库。WebappClassLoader和前面提到的StandardClassLoader都继承自java.net.URLClassLoader，直接从指定目录中读取jar包或class文件。WebappClassLoader直接通过管理Servlet类的字节流来实现Servlet的加载然后缓存。</p>
<p>一般的Servlet只有一个实例，若是实现了STM（SingleThreadModel接口，已Deprecated）的Servlet则会有多个实例缓存着。</p>
<p>但似乎无论怎么设计，都无法避免Web应用程序中的Servlet可以获取到Tomcat启动库中的Bootstrap类（这个包由JVM自带的AppClassLoader加载），默认情况下还可以获取到Tomcat主类库中的所有类（比如Catalina类，因为都是由commonLoader加载，而它又是WebappClassLoader的parent）。尝试过将Tomcat的lib目录中关于Tomcat的类库放到server目录下并配置server.loader属性值（conf/catalina.properties）启动catalinaLoader，这样可以避免Servlet访问到Tomcat主类库，但是这样勉强的同时也阻止了比如ContainerServlet接口（使Servlet能访问Wrapper容器）的运作。</p>
<p>这样的一个坏处就是，可以在Web应用程序的Servlet中获取到Catalina类或者Bootstrap类，再启动一次Tomcat。Servlet中，如果在service方法中这么干会被Tomcat的安全机制给发现使得Tomcat整个关闭，若是配置了loadOnStartup后在init方法中这么干则会引发无限循环导致死机&#8230;</p>
<p>不过通过对Tomcat的ClassLoader的学习，对Java的ClassLoader机制有了大大的实践的了解，无论Tomcat是否真正实现了它设计上的初衷，整个设计思想是值得学习的。</p>
<h2>backgroundProcessor()和后台线程</h2>
<p>Engine容器和Context容器都会通过启动一个后台线程间断执行backgroundProcessor方法，比如重新加载Web应用程序的类文件和资源、扫描Session过期等。</p>
<h2>静态资源</h2>
<p>向Tomcat请求静态资源，和请求Servlet没有太大区别，仍然通过Connector和Container这一流程后，交到Tomcat内部的DefaultServlet来处理读取文件内容并写回客户端，下一次请求会返回304 Not Modified。</p>
<p>所以Tomcat对静态资源的管理比较差速度比较慢，才有apache + tomcat结合的说法吧。</p>
<h4  class="related_post_title">看看 ClassLoader , HTTP , Servlet , Socket , Tomcat , 事件驱动 , 多线程 , 容器 , 生命周期</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>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>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-03-01 -- <a target="_blank" href="http://blog.11034.org/2016-03/simpledateformat_thread_not_safe.html" title="jdk中SimpleDateFormat的实例线程不安全">jdk中SimpleDateFormat的实例线程不安全</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/2013-05/tomcat6.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>HashMap多线程中引发的死循环</title>
		<link>http://blog.11034.org/2012-10/hashmap_concurrent_bug.html</link>
		<comments>http://blog.11034.org/2012-10/hashmap_concurrent_bug.html#comments</comments>
		<pubDate>Mon, 01 Oct 2012 08:55:06 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[多线程]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1328</guid>
		<description><![CDATA[串叔发现的问题，觉得很有意思，已有文章作详细描述，把原文翻译地讲一下。原文：http://mailinator [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>串叔发现的问题，觉得很有意思，已有文章作详细描述，把原文翻译地讲一下。原文：http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html。</p>
<p>Java的HashMap本身就不支持多线程，是线程不安全的，不过代码中有一些机制来阻止多线程中的不安全因素，比如在多线程修改操作中抛出ConcurrentModificationException，但是HashMap忽视了数组扩容时候这一修改操作，从而导致可能在多线程中进行了不安全的操作。<span id="more-1328"></span></p>
<p>java.util.HashMap 的 transfer方法：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">void</span> transfer<span style="color: #009900;">&#40;</span>Entry<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> newTable<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	Entry<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> src <span style="color: #339933;">=</span> table<span style="color: #339933;">;</span>
	<span style="color: #000066; font-weight: bold;">int</span> newCapacity <span style="color: #339933;">=</span> newTable.<span style="color: #006633;">length</span><span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> j <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> j <span style="color: #339933;">&lt;</span> src.<span style="color: #006633;">length</span><span style="color: #339933;">;</span> j<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		Entry e <span style="color: #339933;">=</span> src<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>e <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>
			src<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span> <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;">do</span> <span style="color: #009900;">&#123;</span>
<span style="display:block;background-color: #ffc;">				Entry next <span style="color: #339933;">=</span> e.<span style="color: #006633;">next</span><span style="color: #339933;">;</span></span>				<span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> indexFor<span style="color: #009900;">&#40;</span>e.<span style="color: #006633;">hash</span>, newCapacity<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				e.<span style="color: #006633;">next</span> <span style="color: #339933;">=</span> newTable<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
				newTable<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> e<span style="color: #339933;">;</span>
				e <span style="color: #339933;">=</span> next<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span>e <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</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: #009900;">&#125;</span></pre></td></tr></table></div>

<p>假设现在HashMap中的数组的size为2（桶0和桶1），现在有2个Entry A{key=3}和 B{key=7}，于是都位于桶1中形成链表，假设A在B之前，就是array[1]=&gt;A=&gt;B=&gt;null。</p>
<p>假设有2个线程T1和T2同时进行扩容操作，即会同时调用transfer方法。假设T1先于T2抢到CPU执行transfer方法执行到第9行后切换到T2执行，然后T2顺利执行完毕，然后T1继续执行完毕，看一下这个过程的具体。</p>
<p>T1先执行到第9行完毕，变量e=A，next=B。</p>
<p>T2顺利执行完毕，新的数组size为4（桶0-3），A和B还是hash到了同一个桶中，变为T2_newArray[3]=&gt;B=&gt;A=&gt;null，此时A.next=null，B.next=A。</p>
<p>T1继续执行，第一次循环完毕，T1_newArray[3]=&gt;A=&gt;null，e=B，next=B，e!=null进入下一个循环，此时A.next=null，B.next=A。</p>
<p>第二次循环完毕，T1_newArray[3]=&gt;B=&gt;A=&gt;null，e=A，next=A，e!=null进入下一个循环，此时A.next=null，B.next=A。</p>
<p>第三次循环完毕，<strong>T1_newArray[3]=&gt;A&lt;=&gt;B</strong>，e=null，next=null，e==null跳出循环，此时<strong>A.next=B，B.next=A</strong>。此时桶3的EntryA和EntryB的next指针互相指向对象，没有一个指向null。</p>
<p>因为T1后执行完毕，HashMap采用T1_newArray作为新的array（见HashMap的resize方法）。</p>
<p>T1和T2的扩容操作完毕，并没有引发任何异常和错误，只是埋下了隐患。</p>
<p>当HashMap继续执行其他get和put操作时，并且得到的hash值为3，即在桶3上进行操作问题就出现了，会进入无限循环，而没有抛出任何异常。</p>
<h2>总结</h2>
<p>不要信任HashMap防止多线程错误的代码，不能对HashMap进行多线程修改的操作，并发读是可以的。</p>
<p>对HashMap进行线程安全的办法有很多，比如使用java.util.concurrent.ConcurrentHashMap或者使用java.util.Collections.synchronizedMap(Map&lt;K,V&gt; m)方法包装HashMap，或者在自己调用HashMap的方法中使用同步机制。</p>
<h4  class="related_post_title">看看 多线程</h4><ul class="related_post"><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-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/2012-10/hashmap_concurrent_bug.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Play框架中Action的参数绑定和验证</title>
		<link>http://blog.11034.org/2012-05/param_binding_in_play_framework.html</link>
		<comments>http://blog.11034.org/2012-05/param_binding_in_play_framework.html#comments</comments>
		<pubDate>Mon, 14 May 2012 16:28:31 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Play]]></category>
		<category><![CDATA[框架]]></category>
		<category><![CDATA[验证]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1158</guid>
		<description><![CDATA[再来讲讲Play框架中Action的参数绑定，用过的都说好，哈哈。不仅可以将任意字符串值（比如用户名和密码）、 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>再来讲讲Play框架中Action的参数绑定，用过的都说好，哈哈。不仅可以将任意字符串值（比如用户名和密码）、日期值（生日）、整数值（第几页、年龄等）直接作为Action方法的参数放入其中自动绑定，还可以对继承自play.db.jpa.JAPBase的Model类进行直接的封装，这对一个具有很多变量的JavaBean来说实在是一件令人惊喜的事情，省略了太多太多重复又难看的代码有木有！<span id="more-1158"></span></p>
<p>另外一个令人感觉兴奋的就是参数验证机制，再也不用自己针对Model的每一个属性值去写判断检验的代码，也不用像Struts那样要写复杂的xml文件去作验证，现在的框架十分流行Annotation，只要在Model类中给每个属性上一个类似@Required、@Email、@Phone、@MaxSize之类的注解，剩下的工作Play全部自动会完成了，你最后需要的只是给这个Model类传入Action方法时加一个@Valid注解或者在Action方法中调用validation.valid(obj)就OK了。</p>
<p>其实方法动态参数绑定这点在Spring MVC中也已经有了，但是一般只用来传入HttpServletRequest和HttpServletResponse。</p>
<p>当执行到play.mvc.ActionInvoker.invoke()时，在调用完resolve(request, response)后Play的路由工作已经完成，具体可参见<a title="Play框架的Router机制和jregex包" href="/2012-05/router_in_play_framework.html" target="_blank">Play框架的Router机制和jregex包</a>一文。然后得到了需要调用的Action方法的Method类，在真正调用Action前，就需要执行完参数绑定的工作，如果参数带有@Valid或者@As注解，还要进行验证工作。代码继续执行：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	Play.<span style="color: #006633;">pluginCollection</span>.<span style="color: #006633;">beforeActionInvocation</span><span style="color: #009900;">&#40;</span>actionMethod<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>这句话调用Play的插件机制，在调用Action方法前进行各种操作，算是一个Hook，这在Wordpress中非常常见。其中有一个插件play.data.validation.<strong>ValidationPlugin</strong>，它的工作在beforeActionInvocation(actionMethod)中会检查这个Action的所有参数是否带有任何注解，若有则在这里启动参数绑定和验证工作，若无则直接返回。</p>
<p>不管ValidationPlugin是否提前完成了工作，ActionInvoker.invoke()代码继续进行来到invokeControllerMethod(actionMethod)，然后在里面继续进行参数绑定和验证工作。</p>
<p>前面无论是ValidationPlugin还是后来调用的invokeControllerMethod(actionMethod)，真正进行参数绑定和验证工作的地方都在它们都会调用的</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	ActionInvoker.<span style="color: #006633;">getActionMethodArgs</span><span style="color: #009900;">&#40;</span><span style="color: #003399;">Method</span> method, <span style="color: #003399;">Object</span> o<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>此函数先判断参数是否已经完成绑定工作（即之前是否已被ValidationPlugin调用过了），然后真正进入参数绑定和验证工作。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	<span style="color: #003399;">Object</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> rArgs <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Object</span><span style="color: #009900;">&#91;</span>method.<span style="color: #006633;">getParameterTypes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">length</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> method.<span style="color: #006633;">getParameterTypes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">Class</span> type <span style="color: #339933;">=</span> method.<span style="color: #006633;">getParameterTypes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		...
		<span style="color: #006633;">rArgs</span><span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> Binder.<span style="color: #006633;">bind</span><span style="color: #009900;">&#40;</span>...<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>这个循环遍历Action Method中的每一个参数，然后通过play.data.binding.Binder类来执行具体的参数绑定工作。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	result <span style="color: #339933;">=</span> Play.<span style="color: #006633;">pluginCollection</span>.<span style="color: #006633;">bind</span><span style="color: #009900;">&#40;</span>parentParamNode, name, clazz, type, annotations<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>首先这条语句再次遍历Play的插件集合来实现参数绑定，其中的play.db.jpa.<strong>JPAPlugin</strong>会跳出来完成参数是<strong>JPABase</strong>子类（一般为models包下的Model类）的参数绑定。这里会特判一下参数中是否带有id属性，若有则执行一条数据库查询获得此Model的这条数据的Object，若无则通过反射拿到无参构造重新new一个Object。然后就会调用play.db.jpa.GenericModel.edit()方法将参数绑定到这个Object中。在edit()方法中，通过反射获取JavaBean所有的Field（这边的Field若带有外键性质的注解，如@OneToOne，会触发递归绑定），绕了一圈最后调用Binder.internalBind()完成对每个Field的绑定。</p>
<p>如果参数不是JPABase的子类只是Java中的普通类，则直接调用Binder.internalBind()来搞定就好了。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	Binder.<span style="color: #006633;">internalBind</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span></pre></td></tr></table></div>

<p>这个方法完成了对Java基本类型和一些常用的集合类、属性类的值绑定。</p>
<ol>
<li>若Field是Enum或其子类，调用Binder.bindEnum()，由Enum.valueOf()实现</li>
<li>若Field是Map或其子类，调用Binder.bindMap()</li>
<li>若Field是Collection或其子类，调用Binder.bindCollection()</li>
<li>若Field是数组，调用Binder.bindArray()</li>
<li>都不是，则调用Binder.directBind()</li>
<li>在directBind中，会从Map&lt;Class&lt;?&gt;, TypeBinder&lt;?&gt;&gt; Binder.supportedTypes中寻找是否是Play指定的特殊的可以绑定的Java类，比如java.util下的Date、DateTime、Calendar或者java.io.File等，具体见Binder类的static初始化域</li>
<li>若也不是上面这些稍复杂的类型，那就是Java的基本类型了，按照String、Character、int或Integer、long或Long、double或Double、BigDecimal、boolean或Boolean这样的顺序依次解析和绑定</li>
<li>在internalBind中会catch住所有抛出的解析异常，从而调用Validation.addError()完成验证中添加错误信息的工作</li>
</ol>
<p>这些类型的数据绑定中，若数据值为空，则相应地返回对应类型的默认值（0、0.0、false、new Object()等）。</p>
<p>至于参数验证，很简单地遍历每个Field根据其拥有的注解一个一个地去自动检查就好了，这没有难度，无论是@Valid注解还是手动调用validation.valid()方法都是差不多的。</p>
<p>的确还是很复杂，涉及到的代码也很多，所以当一个JavaBean的属性值不是太多时，还是建议一一获取属性值自己来组建Model类的方式比较好，一是效率更高，二是Html模板中也不用在input的name属性值中用model.field这种形式，感觉不是很好。</p>
<h4  class="related_post_title">看看 Play , 框架 , 验证</h4><ul class="related_post"><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><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></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/2012-05/param_binding_in_play_framework.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Play框架的Router机制和jregex包</title>
		<link>http://blog.11034.org/2012-05/router_in_play_framework.html</link>
		<comments>http://blog.11034.org/2012-05/router_in_play_framework.html#comments</comments>
		<pubDate>Mon, 14 May 2012 16:23:37 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Play]]></category>
		<category><![CDATA[框架]]></category>
		<category><![CDATA[正则]]></category>
		<category><![CDATA[路由]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1154</guid>
		<description><![CDATA[最近对Play框架兴趣甚浓，对其有兴趣的功能都想一探究竟，先研究了下它的Router机制。概要地说，主要使用r [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>最近对Play框架兴趣甚浓，对其有兴趣的功能都想一探究竟，先研究了下它的Router机制。概要地说，主要使用routes文件和正则表达式来完成URL到函数的映射，不过这里正则表达式功能比较强可以解析出{id}这样的参数，因此Play使用了第三方jregex包而不是JDK的java.util.regex包来执行正则表达式的工作。<span id="more-1154"></span></p>
<p>Play的routes文件很清楚简单，每行一个Route，最重要的参数包含请求方式、请求URL和响应的函数，就像下面这一行：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	GET	<span style="color: #339933;">/</span>customer<span style="color: #339933;">/</span>detail<span style="color: #339933;">/</span><span style="color: #009900;">&#123;</span>id<span style="color: #009900;">&#125;</span>	CustomerController.<span style="color: #006633;">detail</span></pre></td></tr></table></div>

<p>Router机制机制具体包含解析和路由2个阶段。路由代码主要在play.mvc.Router和其静态内部类Route，解析Request代码主要在play.mvc.ActionInvoker中。</p>
<h3>1.route文件的解析和路由表的建立</h3>
<ol>
<li>Play在启动初始化时，通过application.conf配置文件获取http.path参数，ctxPath = http.path;（这个就是相对路径啦）</li>
<li>调用Router.detectChanges(ctxPath);//若路由表没有建立或者routes文件被更新，解析文件</li>
<li>针对route文件的每一行（所以路由的顺序按照routes文件行的顺序为优先级），根据正则表达式获取method、path、action值，可能还有headers和params值</li>
<li>新建一条Route记录，并作一些解析计算（比如找出path中带有的参数信息）</li>
</ol>
<p>以上面提到那条示例的route为例，method = GET，path = /customer/detail/{id}，action = CustomerController.detail，在新建Route记录作解析计算时，会先将path利用正则表示式转化为/customer/detail/{&lt;[^/]+&gt;id}的形式，然后拿出所有的{id}形式的变量信息，再然后继续通过正则表达式再转化为</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;">	<span style="color: #339933;">/</span>customer<span style="color: #339933;">/</span>detail<span style="color: #339933;">/</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span>id<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#91;</span><span style="color: #339933;">^/</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span><span style="color: #009900;">&#41;</span></pre></td></tr></table></div>

<p>这样的形式，这也是path最后的pattern，用来去匹配请求的URL。这个形式的正则表达式，java.util.regex包无法解析，而jregex包是可以处理的，并且group的时候可以根据变量名来取变量值，很方便。</p>
<h3>2.URL的路由</h3>
<p>当一个Http请求过来，就遍历路由表，当一条Route记录可以匹配当前的path，就可以获得在routes文件中配置的action值，就比如上面的CustomerController.detail，并通过正则表达式获得路径中的参数（id=?）。然后解析action值得到Controller类名和Action方法名（action值若不以“controllers.”开头的话会被自动加上），通过Play框架中的ApplicationClassloader获取到Controller类的Class类，然后通过此Class类反射获取到具体Action的Method，将这些解析的结果都赋给play.mvc.Http的静态内部类Request中，URL路由就完成了。</p>
<h3>值得优化</h3>
<p>这样看来，其实这自定义URL的路由机制还是蛮简单的，如果去除了动态参数绑定、静态文件路由特判等一些特殊功能的话，那真的是很简单很容易实现了。而且感觉Play的路由代码还有很多可以值得优化：</p>
<ol>
<li>不带动态参数的路由路径（就是纯字符串匹配）可以通过HashMap来匹配，而不用每条都去执行一遍正则表达式</li>
<li>每条路由对应的Controller类和调用的Action Method应该在路由表建立的时候就应该被解析完毕并存储在路由表中，而不是每一个http请求都重新解析一遍</li>
</ol>
<h3>jregex包</h3>
<p>看Play源代码发现了这个包，官方网址：<a href="http://jregex.sourceforge.net/">http://jregex.sourceforge.net/</a>。网站上很好地比较了它与java.util.regex的优劣，用变量名去Group即网站上介绍的named groups，而且据它自己介绍性能比较高。觉得当一个应用大量用到正则表达式的话，jregex是比较推荐的。</p>
<p><strong><code>JRegex</code></strong> vs. <strong><code>java.util.regex</code></strong></p>
<ul>
<li>(+) JRegex is a Free Software</li>
<li>(+) JRegex is portable (runs under any version of java)</li>
<li>(+) JRegex supports more features (named groups, Perl5.6&#8217;s conditional statements and more)</li>
<li>(-) java.util.regex implements 2&#8217;d level of Unicode support (JRegex is level 1)</li>
<li>(-) JRegex is not a &#8216;standard&#8217; library</li>
</ul>
<h4  class="related_post_title">看看 Play , 框架 , 正则 , 路由</h4><ul class="related_post"><li>2012-05-15 -- <a target="_blank" href="http://blog.11034.org/2012-05/param_binding_in_play_framework.html" title="Play框架中Action的参数绑定和验证">Play框架中Action的参数绑定和验证</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>2012-06-28 -- <a target="_blank" href="http://blog.11034.org/2012-06/get_video_cover_image.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/2012-05/router_in_play_framework.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Java Play framework</title>
		<link>http://blog.11034.org/2012-05/java_play_framework.html</link>
		<comments>http://blog.11034.org/2012-05/java_play_framework.html#comments</comments>
		<pubDate>Sun, 13 May 2012 05:19:29 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Play]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[框架]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1150</guid>
		<description><![CDATA[因本科几个老同学的邀请参与一个项目的开发，用到了这个全新的Java框架，“Play”。名字很有意思，接触的1. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>因本科几个老同学的邀请参与一个项目的开发，用到了这个全新的Java框架，“Play”。名字很有意思，接触的1.2.4版本而不是最近的2.o，上手很快，安装包里有自带的Sample程序。框架的易用程度大大超过了自己的预期，果然随着时代的发展Java框架越来越强大并且越来越人性化，最大程度地方便程序员开发应用。</p>
<p>一直使用也看好Spring MVC，但是Spring的日益臃肿让这个框架的优势在不断流失。列举Play一些非常棒的地方，特别是较之Spring MVC的优势。</p>
<p><span id="more-1150"></span></p>
<ul>
<li>Play最大的一个特点就是开发期间每次修改Java代码无需重启Java Web服务器，瞬间感觉和写PHP程序一样方便！这个真的是太爽，之前写Java Web程序调试，每次关闭再重启Tomcat少说也得5秒钟吧。</li>
<li>配置文件齐全，基本上帮你考虑到了所有可能要配置的内容（配置、持久、缓存、日志、国际化等）</li>
<li>URL route很清楚很简单，而且完全自定义URL Pattern（SpringMVC的Route还是不算太好，不能定义/model/action这样的url）</li>
<li>一个Java类作为一个Controller可以每个方法都对应一个HTTP请求的方式，大量减少了类的数量</li>
<li>Controller的方法和Template文件对应很好，默认模式下自动对应，十分方便</li>
<li>根据Action方法的参数类型和名称自动从提交参数中自动封装，包括对JavaBean的封装，对大量属性的表单特别有优势</li>
<li>对传入参数的验证很容易而且基本不用再自己写代码，全部在Model类里用Annotation搞定</li>
<li>Action方法可方便返回Html、Text、Json、Xml等数据，各种方便的API</li>
<li>Model层真的简略到基本不用写任何代码了，利用Hibernate自动建表、插入和更新自不用说，因为所有Model从特定类继承而来，丰富的API基本够平常所需，而且查询接口十分简单和强大。再也不用像SpringMVC那样每一个Model都要建一个Dao，每一个查询都要写一个十分复杂的Hibernate方法。</li>
</ul>
<p>Play框架已经不仅仅是个框架的概念了，而是一个开发环境了，因为要利用它帮你生成Project，eclipsify进入开发模式，run进入debug模式，war进入发布模式，而且Play自定义了一套Project下的目录结构和文件名称，这些一经修改很有可能运行就会出错。这是一把双刃剑，虽然方便了开发者和初学者，但是对于比较喜欢自己完全掌控代码和框架运作的开发人员来说并不是一件好的事情。</p>
<p>当然，也感觉Play有些还不太习惯的地方，较之其他框架另类之处，还有一些有待改善的地方。</p>
<ul>
<li>包结构名默认被限定，不符合大众模式（域名倒置的形式）</li>
<li>一个Controller不能自定义对应的Template的目录名，而默认采用与Controller名一致的目录名（强烈建议应该在Controller上加一个Annotation来自定义Template的目录名）</li>
<li>View模式下的模板语法（Groovy）据说效率比较低</li>
<li>开发模式下，因为靠Play自带程序运行，脱离IDE环境，较难实现单步跟踪</li>
<li>相比Spring，加入其它第三方组件不太方便，比如更换模板引擎等</li>
</ul>
<div>总的来说，Play让人耳目一新的感觉，第一感觉是非常棒的，真的大大加快了用Java开发Web的速度和效率，而且Play作为框架的理念也理解的很好很懂得开发人员的心思来尽量满足需求，当然个人觉得这甚至有点过了因为什么都帮你干完了开发人员的价值很多就体现不出来了。作为一个demo或者小型应用，Play绝对适合，但是要开发一个企业级大项目，就至今对Play的了解来看，也许Spring这一套路更稳重一些。</div>
<h4  class="related_post_title">看看 Play , Spring , 框架</h4><ul class="related_post"><li>2012-05-15 -- <a target="_blank" href="http://blog.11034.org/2012-05/param_binding_in_play_framework.html" title="Play框架中Action的参数绑定和验证">Play框架中Action的参数绑定和验证</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><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>2013-08-10 -- <a target="_blank" href="http://blog.11034.org/2013-08/some_skills_in_java.html" title="学到的一些东西">学到的一些东西</a></li><li>2011-03-29 -- <a target="_blank" href="http://blog.11034.org/2011-03/java_encoding.html" title="Java编码的那些事儿">Java编码的那些事儿</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/2012-05/java_play_framework.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>java.util中的集合类解析</title>
		<link>http://blog.11034.org/2012-03/java_util_collections.html</link>
		<comments>http://blog.11034.org/2012-03/java_util_collections.html#comments</comments>
		<pubDate>Wed, 21 Mar 2012 16:15:37 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[数据结构]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1101</guid>
		<description><![CDATA[Java源码学习的第二个重点块，也是jdk中最有意义最有价值的一块，这里的集合类都是数据结构和算法，上手快学到 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Java源码学习的第二个重点块，也是jdk中最有意义最有价值的一块，这里的集合类都是数据结构和算法，上手快学到的东西也多，而且在日常Java代码中出现率仅次于java.lang包。java.util中集合类必须是最重要的一块了，当然其他还有日期日历时区、货币、线程等工具类，不过这些类并没有太大的价值去精读。<br />
<span id="more-1101"></span></p>
<p>java.util中的集合类整体，在javadoc中被称为Java Collections Framework，详细可wiki。</p>
<p>主要有以下线路：(I)表示接口，(A)表示抽象类</p>
<ul>
<li>(I)<span style="color: #993366;">Iterable</span>-&gt;(I)<span style="color: #993366;">Collection</span>-&gt;(I)<span style="color: #993366;"><strong>List </strong></span>[ArrayList、LinkedList、Vector、Stack]</li>
<li>(I)<span style="color: #993366;">Iterable</span>-&gt;(I)<span style="color: #993366;">Collection</span>-&gt;(I)<span style="color: #993366;"><strong>Set </strong></span>[HashSet、LinkedHashSet、TreeSet]</li>
<li>(I)<span style="color: #993366;">Iterable</span>-&gt;(I)<span style="color: #993366;">Collection</span>-&gt;(I)<span style="color: #993366;"><strong>Queue </strong></span>[ArrayDeque、PriorityQueue]</li>
<li>(I)<strong><span style="color: #993366;">Map</span> </strong>[HashMap、LinkedHashMap、IdentityHashMap、TreeMap、WeakHashMap]</li>
<li>(A)<span style="color: #008000;">Dictionary</span>-&gt;Hashtable</li>
<li>BitSet、<span style="color: #ff9900;">Arrays<span style="color: #000000;">、</span>Collections</span></li>
</ul>
<h2><span style="color: #993366;">List</span></h2>
<h4><span style="color: #0000ff;">ArrayList&lt;E&gt;</span></h4>
<p>由数组实现的链表，每次调用add(E e)时调用ensureCapacity(size + 1)，可能导致元素数组扩容1.5倍。因为它是随机访问的（即实现RandomAccess接口），javadoc中表示使用for(int i = 0)的循环遍历会比Iterator遍历和foreach()循环快（foreach循环即Iterator遍历），这也在很多源码中体现出来若实例为RandomAccess的，会采取for循环来遍历。</p>
<h4><span style="color: #0000ff;">LinkedList&lt;E&gt;</span></h4>
<p>由结点链接而成的链表，API较多用来当做栈、队列、双向队列都可以。</p>
<h4><span style="color: #0000ff;">Vector&lt;E&gt;</span> 和 <span style="color: #0000ff;">Stack&lt;E&gt;</span> extands Vector&lt;E&gt;</h4>
<p>Vector类似ArrayList，但是此为线程安全版本。小有区别的，每次需要扩容时元素数组扩容2倍。</p>
<p>Stack就是个线程安全的栈，没啥好说。</p>
<h2><span style="color: #993366;">Queue</span></h2>
<h4><span style="color: #0000ff;">ArrayDeque&lt;E&gt;</span> implements Deque&lt;E&gt;</h4>
<p>由数组实现的双向队列（Deque接口）。这个类的代码实现有点意思的，元素数组大小一定是power-of-2，然后可以方便地使用位运算来判断长度溢出等。因为要保持数组容量的这个特性，数组扩容代码也挺有意思，有个简单的算法将某个数二进制中最高位1右边所有的位全部置为1，然后再+1，即得到了一个更大的power-of-2。</p>
<h4><span style="color: #0000ff;">PriorityQueue&lt;E&gt;</span></h4>
<p>由数组实现的优先队列，元素E必须实现Comparable接口或选择带有Comparator的PriorityQueue构造，因为在比较元素E时会强制转化类型为Comparable。看这个类的源代码可以好好复习下优先队列及满二叉树的实现。数组扩容的代码有点意思，当原容量&lt;64时，扩容2倍，否则扩容1.5倍，综合ArrayList和Vector的扩容方案。</p>
<h2><span style="color: #993366;">Set</span></h2>
<p>set是Java Collections Framework最坑爹的实现了，因为它根本没有任何实现，所有的set都在内部保存有一个对应的map类型实例&#8230;</p>
<h2><span style="color: #993366;">Map</span></h2>
<p>map是Java Collections Framework最复杂的一块。</p>
<h4><span style="color: #0000ff;">HashMap&lt;K, V&gt;</span></h4>
<p>一般的Hash表实现，冲突机制使用在原位置接链表的方式。与教科书推荐的不太一样的是，桶的个数并不是推荐的质数个（据说这样可以减小冲突率）而是power-of-2，为的是通过位操作的代码高效实现。</p>
<ul>
<li>默认加载因子是0.75，即75%的桶被沾满了就会扩容重新hash，每次double容量</li>
<li>冲突机制：接链表</li>
<li>hash函数，static int hash(int h)，对元素E.hashCode()再进行一次hash，hash算法很复杂各种移位抑或操作，表示看不懂&#8230;</li>
<li>允许null键，而且null键必定位于0号位桶</li>
<li>键的比较采用Object.equals()方法（null键带有特判）</li>
<li>foreach循环或Iterator遍历，按照桶的顺序（当一个桶有多个键时，按照插入顺序遍历）</li>
</ul>
<h4><span style="color: #0000ff;">Hashtable&lt;K, V&gt;</span></h4>
<p>姑且算放在Map这里吧，主要可以和HashMap来个对比。算是线程安全的HashMap，但是相比来说代码没有HashMap优化的好。</p>
<p>Hashtable与HashMap的不同之处：</p>
<ul>
<li>Hashtable继承自Dictionary抽象类；HashMap继承自AbstractMap抽象类</li>
<li>Hashtable的hash函数直接使用的E.hashCode()，使用取余操作；HashMap使用位操作并进一步hash</li>
<li>Hashtable不允许null键，也不允许null值；HashMap都允许</li>
<li>Hashtable中是contains()方法；HashMap是containsKey()或containsValue()</li>
<li>Hashtable中由Enumerator来实现Enumeration和Iterator；HashMap则单由Iterator实现</li>
</ul>
<h4><span style="color: #0000ff;">LinkedHashMap&lt;K, V&gt;</span> extends HashMap&lt;K, V&gt;</h4>
<p>数据存储和读取和HashMap无异，但是多了一个链表来保存插入键的顺序。当foreach循环或Iterator遍历时，会按照这个链表来遍历。比较有意思的是，LinkedHashMap为后续提供了一个LRU容器的选择，可以自建个类继承自LinkedHashMap，重写protected boolean removeEldestEntry()然后return true。</p>
<h4><span style="color: #0000ff;">IdentityHashMap&lt;K, V&gt;</span></h4>
<p>对外而言这个Map与HashMap最大的不同在于比较Key用==操作符，其实内部实现有很大的区别。同样使用power-of-2大小的元素数组，但是没有用一个内部静态类去封装Key和Value，用偶数桶存放Key奇数桶存放Value，即每一项占用2个桶，然后冲突策略使用的index + 2策略，即冲突就往下一个最近的桶放。</p>
<ul>
<li>冲突机制：开放定址法，并为线性探测；效率低、删除元素非常麻烦</li>
<li>hash函数：对System.identityHashCode(E)继续一些位操作来得到hash值</li>
<li>允许null键</li>
<li>键比较采用==操作符</li>
<li>foreach循环或Iterator遍历，按照桶的顺序</li>
<li>OO封装不好，key、value连着放除了性能上很小的提升外没有啥优点</li>
</ul>
<h4><span style="color: #0000ff;">TreeMap&lt;K, V&gt;</span></h4>
<p>由红黑树实现的Map，Key必须实现Comparable接口或选择带有Comparator的TreeMap构造。也是非常值得精读的一个类，不仅仅在于了解一下红黑树这个效率比较优秀的平衡二叉树的实现更在于对一般的二叉树这个数据结构和算法的掌握。为了看懂源码学习了一会儿红黑树，真心表示复杂，所以觉得大致了解红黑树就可以了无需看懂插入元素和删除元素的每一步操作这么具体。因为TreeMap是唯一实现SortedMap接口的Map，很多API的代码实现对二叉树的操作十分的美，特别是static successor()、static predecessor()、getCeilingEntry()、getFloorEntry()的实现很关键。</p>
<ul>
<li>拒绝null键，因为键需要比较，但允许null值</li>
<li>树的重建，用最平衡的方法重建红黑树，递归则重建左子树、中间节点、右子树。而且红色节点计算很方便，最后一层（若没有满）则所有节点都是红色的（即保证所有的黑色节点为一棵满树）</li>
</ul>
<h4><span style="color: #0000ff;">WeekHashMap&lt;K, V&gt;</span></h4>
<p>基本和HashMap一样，就是Entry的实现继承自WeakReference，key是弱引用，value是强引用。适用于元素量巨大，内存可能不足够用的时候，自动GC某些key并在下次访问Map时删除这些映射。适合作缓存，某些项突然消失也无关紧要的那种。</p>
<ul>
<li>Entry&lt;K,V&gt; extends WeakReference&lt;K&gt;，当GC工作时某些Key会被GC并放入WeekHashMap的ReferenceQueue中。每次对WeekHashMap的调用，会检查ReferenceQueue中已被GC的Key，然后找到那项Entry将里面的强引用Value给设置为null便于GC，并删除Map中对此Entry的引用，更新计数。</li>
<li>键冲突机制、hash函数、null键、键比较、遍历全部与HashMap相同</li>
</ul>
<h2><span style="color: #0000ff;">BitSet</span></h2>
<p>使用long数组来实现的位向量，使用了大量位运算，值得一读。</p>
<h2><span style="color: #0000ff;">Arrays</span></h2>
<p>全静态方法的针对数组的工具类。主要提供了排序、二分搜索、复制、填充等功能。</p>
<p>排序方法中：</p>
<ul>
<li>所有Java基础类型的数组排序使用QuickSort，在元素量&lt;7时使用InsertSort。选取pivot有一定改进。</li>
<li>对double和float数组的排序有预处理，将NaN放到数组最后不参与排序，将-0.0d设为0.0d参与排序</li>
<li>对Java类型的数组排序使用MergeSort，在元素量&lt;7时使用InsertSort。改进：当2个子序列a和b，max(a)&lt;=min(b)则直接copy a和b至目标数组</li>
</ul>
<h2><span style="color: #0000ff;">Collections</span></h2>
<p>全静态方法的针对集合类的工具类。主要提供排序、元素移位、封装集合为不可变集合、封装集合为线程安全集合、封装集合为类型检查集合等功能。</p>
<p>所有的集合封装方法，都是将传入的集合封装一层类，然后在外层提供的API中拒绝修改方法（抛出UnsupportedOperationException）、提供线程安全（synchronized关键字）、检查插入类与泛型类来实现。</p>
<h2>综合</h2>
<ul>
<li>所有的集合类都有modCount这个field，它的作用是记录集合实例被改变的次数，特别是调用Iterator时这个modCount会被保存副本，在遍历过程中若发现集合实例被修改（即副本modCount与集合类实例的modCount不相等），立即停止遍历并抛出异常，即javadoc中提到的<tt>iterator</tt> 和 <tt>listIterator</tt> 方法返回的迭代器是快速失败的。</li>
<li>Map和Set中支持KeySet、ValueSet、EntrySet这些方法，返回的Set并不是一个独立的Set，内部无任何元素只有一个指向原来集合实例的指针（内部类），所以一般只用来作遍历用，如果修改这些Set会同步地修改原来集合实例。</li>
<li>所有的集合类实现了java.lang.Cloneable接口，从而支持clone()方法，然后又全部重写clone()方法，集合实例的确被clone了，但是内部包含的所有集合元素只是copy了引用，而没有完整copy一份（当然因为元素本身不一定实现了Cloneable不能被clone），因为本质只是调用了System.arraycopy()而已。</li>
</ul>
<h2>感想</h2>
<ul>
<li>大多数集合类的实现其实一点都不难，慢慢改进调试我们也写得出来</li>
<li>有很好的OO思想直接体现在整个Java Collections Framework的层次结构设计，是值得学习的</li>
<li>Iterator思想也是很值得学习的，因为这一点也许接触地更少</li>
<li>也许算Java语言的一个无奈之处，因为基础类型没有共同可替代的方式，所以针对基础类型的方法全部要重载一遍，而不像针对类类型只要写一个Object版本就可以。这导致基础类型的方法中，比如System.out.println()，比如Arrays的所有方法，代码大量冗余重复，这是非常不美的地方啊，不知道以后的Java会不会改进</li>
</ul>
<h4  class="related_post_title">看看 oop , 数据结构</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>2013-10-31 -- <a target="_blank" href="http://blog.11034.org/2013-10/hulu.html" title="hulu校招">hulu校招</a></li><li>2012-12-06 -- <a target="_blank" href="http://blog.11034.org/2012-12/ac_automachine.html" title="一个OOP的AC自动机代码">一个OOP的AC自动机代码</a></li><li>2012-07-03 -- <a target="_blank" href="http://blog.11034.org/2012-07/trie_in_php.html" title="敏感词过滤，PHP实现的Trie树">敏感词过滤，PHP实现的Trie树</a></li><li>2010-11-17 -- <a target="_blank" href="http://blog.11034.org/2010-11/python2x_first_impression.html" title="Python2.x的第一印象">Python2.x的第一印象</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/2012-03/java_util_collections.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>java.lang包的一些学习心得</title>
		<link>http://blog.11034.org/2012-03/learning_java-lang.html</link>
		<comments>http://blog.11034.org/2012-03/learning_java-lang.html#comments</comments>
		<pubDate>Wed, 29 Feb 2012 17:39:58 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[jdk]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1078</guid>
		<description><![CDATA[寒假很闲，就打算写代码之余看看JDK源码。别觉得JDK源码是什么很难看不懂的代码，都是Java代码而且以逻辑为 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>寒假很闲，就打算写代码之余看看JDK源码。别觉得JDK源码是什么很难看不懂的代码，都是Java代码而且以逻辑为主算法很少，觉得体验Class、OOP和API的设计远远比掌握某个Class、API的复杂算法有意义(meaningful)也有意思(interesting)的多。</p>
<p>当然因为精力和时间有限，挑选常用和比较重要的包和类去看一下来的实在一些，然后你就会发现java.*下面其实就真没有多少包和类了，10来个包几百个类吧，这些类中有一半是没有或者基本可忽略代码的Annotation、Interface、Exception、Error等，更别看随便一个什么类的代码就上千行，其中3/4是注释有木有！好了，所以这也算是给自己的一个动力吧。果断地拿java.lang开咬&#8230;</p>
<p><span id="more-1078"></span></p>
<p>看了不少jdk代码下来几个感受：</p>
<ol>
<li>Java最强调的代码规范到哪里去了&#8230;代码的括号、缩进真是乱成一团啊，由于是源码在Ecilpse中不能修改不能被Ctrl-Shift-F，这是很不爽的</li>
<li>注释长的很，看英文的注释也提升不了多少英文水平，还是看中文的效率高多了，觉得有不太理解地再去看英文注释（毕竟翻译有差）</li>
<li>大部分关键核心的代码都被native了，所以读源码更没难度了</li>
<li>大部分的代码其实真的很普通，既没有很高深也没有很美观</li>
<li>有些个别的代码写的真是XXX&#8230;（貌似程序猿都有强烈重构他人不完美的代码的冲动&#8230;）</li>
</ol>
<p>然后介绍几个java.lang中一些自我感觉有趣的地方和值得学习的地方。</p>
<h3>装箱机制中的int和Integer</h3>
<p>先做道题，判断下面3个布尔值分别是什么：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #003399;">Integer</span> a1 <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Integer</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003399;">Integer</span> b1 <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Integer</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</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>a1 <span style="color: #339933;">==</span> b1<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003399;">Integer</span> a2 <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
<span style="color: #003399;">Integer</span> b2 <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</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>a2 <span style="color: #339933;">==</span> b2<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003399;">Integer</span> a3 <span style="color: #339933;">=</span> <span style="color: #cc66cc;">200</span><span style="color: #339933;">;</span>
<span style="color: #003399;">Integer</span> b3 <span style="color: #339933;">=</span> <span style="color: #cc66cc;">200</span><span style="color: #339933;">;</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>a3 <span style="color: #339933;">==</span> b3<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>答案是false、true、false。第一次见也许会感觉比较奇怪，这里涉及到int的装箱机制变成Integer，LZ第一次在某个视频中看到这个奇怪的问题然后听主讲人说在-128到127这个范围内装箱机制的结果就是这样的，也没讲具体啥原因。int装箱成Integer是个Java普通对象了，得new出来才行，为什么-128到127之间的装箱产生的Integer地址是一样的呢？</p>
<p>看Integer.java源码即知原因，其实java.lang.xxx（xxx for {Byte, Short, Integer, Long}）都一样。Integer中有一个静态内部类IntegerCache，缓存了从-128到127这256个最常用数的Integer对象，Integer类被加载的时候这些缓存对象就初始化好了。然后只有唯一通过Integer.valueOf(int i)这个API用到了这个IntegerCache，当i在-128到127范围内时，便返回相应的缓存对象，否则就立即new一个。LZ的推理就是，int的装箱调用了Integer.valueOf(int i)这个方法（通过Eclipse的单步跟踪debug可以证明这一点），然后使用了IntegerCache的缓存，就是这样。事实证明，可以利用反射去查看IntegerCache中的为1的那个Integer的对象的地址，和题中的a2和b2的地址都是一样的。</p>
<h3>Integer中的位算法</h3>
<p>Integer类中有许多涉及到位操作的API，如bitCount(int i), numberOfLeadingZeros(int i), lowestOneBit(int i)等，它们的源代码都很难理解显然用到了位操作的某些算法，很好的是注释中提到了这一点：“bit twiddling”方法的实现基于 Henry S. Warren, Jr.撰写的 <em>Hacker&#8217;s Delight</em>（Addison Wesley, 2002）中的一些有关材料。然后Google一下你会发现这是一本好书啊，值得一读（虽然LZ还木有看过）。</p>
<h3>String类的字符串匹配算法</h3>
<p>看到String类的indexOf方法时可紧张了，想想可能是碰到的第一个实现了经典算法的源码API。怕看不懂源码先老老实实去自学字符串匹配的kmp算法，Matrix67的博客上的关于kmp的解说的确浅显易懂（原来这个算法又叫看毛片算法，哈哈哈）。研究半天终于懂了之后，兴奋地去再去读String.indexOf源码了，然后失望地发现其居然只是简单地用了复杂度为M*N的暴力匹配&#8230;这不是坑爹么！</p>
<h3>某些非public的类</h3>
<p>在javadoc中是看不到非public的类的，而某些这些非public的类还是比较重要的。比如java.lang.StringCoding，这是java根据不同的编码解析和生成字节流字符串的类，虽然不太看的懂但很重要的说总要让人知道存在吧，连注释都很简略。</p>
<p>大家都知道StringBuilder和StringBuffer功能一样但后者是线程安全的而前者则不是，而原理是所有的方法实现都在java.lang.AbstractStringBuilder中，二者都继承于这个类，然后对每个API再封装一层，区别就是StringBuilder直接调用super的API，StringBuffer的API则用了synchronized关键字再调用super的API。但是因为同样道理文档木有收录AbstractStringBuilder类，所以在文档中显示前2个类都直接继承自Object，好假&#8230;</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/chenghuangge.html" title="城隍阁一游">城隍阁一游</a></li><li>2015-06-20 -- <a target="_blank" href="http://blog.11034.org/2015-06/xian.html" title="端午前的西安华山游">端午前的西安华山游</a></li><li>2015-10-12 -- <a target="_blank" href="http://blog.11034.org/2015-10/hualien.html" title="我在花莲看日出">我在花莲看日出</a></li><li>2021-05-03 -- <a target="_blank" href="http://blog.11034.org/2021-05/thousand_islands.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-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/2012-03/learning_java-lang.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>配置Hadoop集群的几个问题</title>
		<link>http://blog.11034.org/2011-12/hadoop_cluster_setting.html</link>
		<comments>http://blog.11034.org/2011-12/hadoop_cluster_setting.html#comments</comments>
		<pubDate>Thu, 15 Dec 2011 13:54:41 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[hadoop]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=1033</guid>
		<description><![CDATA[实验室的调研项目，负责调研HBase，在用单机模式和本地文件系统测试之后，需要搭建HBase集群来测试，当然就 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>实验室的调研项目，负责调研HBase，在用单机模式和本地文件系统测试之后，需要搭建HBase集群来测试，当然就要先搭建起来Hadoop集群。花了一天时间，碰到问题若干，并且都一定程度地坑爹，最后终于顺利跑起，泪流满面。</p>
<p>环境：CentOS、hadoop-0.20.203.0rc1.tar.gz、3台虚拟机构建的集群</p>
<h3>问题一：手动mkdir相关目录</h3>
<p><span id="more-1033"></span><br />
比如hdfs-site.xml中的dfs.name.dir项、dfs.data.dir项，mapred-site.xml中的mapred.system.dir项，这些配置的目录都需要事先mkdir好并保证应有的权限。</p>
<h3>问题二：root用户导致的-jvm问题</h3>
<p>使用root用户启动hadoop，会报“无法识别的 -jvm 参数，Java虚拟机启动失败”这样的错误。原因在于$HADOOP_HOME/bin/hadoop文件中的一行，搜索-jvm就能发现，当用户ID为0（即root用户），则会多带一个-jvm项。</p>
<p>为hadoop和mapreduce专门建立账号是官方推荐的做法，懒人可以直接修改上面提到的地方绕过-jvm的问题。</p>
<p>坑爹指数：★★★     评论：虽然用root账户不是推荐的方法，那直接报个Warning好了嘛，直接报错导致启动失败总不是应该的结果吧&#8230;</p>
<h3>问题三：使用start-all.sh导致jobtracker.info写入问题</h3>
<p>LZ在搭建集群过程中，执行start-all.sh后，遇到namenode机器mapred写入jobtracker.info文件失败的问题，具体异常信息为“java.io.IOException : File ***/jobtracker.info could only be replicated to 0 nodes, instead of 1”。</p>
<p>问题在于执行start-all.sh，在启动namenode之后会立即启动mapred，而这时namenode处于30秒的安全状态中，datanode还没有连接上来，于是执行mapred就出错了。解决方法是：不要执行start-all.sh，先执行start-dfs.sh，通过http://yoururl:50070观看hdfs的状态，有明确倒计时说明安全状态，在安全状态结束后，再执行start-mapred.sh，即可。</p>
<p>坑爹指数：★★？     评论：那bin目录下给一个start-all.sh到底是为什么呢？难道是由于哪里配置的问题或是什么的问题？</p>
<p>虽然在百度实习时有搭建过Hadoop集群，不过貌似那时用的Hadoop不像是Apache发布的包而大概是经过百度内部改良的吧，反正和这次下载的包结构有差别。所以基本也算是第一次搭建了，不过万事靠Google、百度基本还是能找到解决，各种求罩求指导！</p>
<h4  class="related_post_title">随便看一看</h4><ul class="related_post"><li>2015-10-12 -- <a target="_blank" href="http://blog.11034.org/2015-10/hualien.html" title="我在花莲看日出">我在花莲看日出</a></li><li>2017-05-12 -- <a target="_blank" href="http://blog.11034.org/2017-05/qinghai.html" title="青海篇：青海湖茶卡盐湖和雅丹地貌">青海篇：青海湖茶卡盐湖和雅丹地貌</a></li><li>2010-07-26 -- <a target="_blank" href="http://blog.11034.org/2010-07/hongkong_travel.html" title="香港10日行，纪念第一次出境">香港10日行，纪念第一次出境</a></li><li>2011-06-13 -- <a target="_blank" href="http://blog.11034.org/2011-06/graduation_design.html" title="毕业设计答辩">毕业设计答辩</a></li><li>2011-07-29 -- <a target="_blank" href="http://blog.11034.org/2011-07/baidu_mobads_online.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/2011-12/hadoop_cluster_setting.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java编码的那些事儿</title>
		<link>http://blog.11034.org/2011-03/java_encoding.html</link>
		<comments>http://blog.11034.org/2011-03/java_encoding.html#comments</comments>
		<pubDate>Tue, 29 Mar 2011 13:15:20 +0000</pubDate>
		<dc:creator><![CDATA[-Flyぁ梦-]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[编码]]></category>

		<guid isPermaLink="false">http://blog.stariy.org/?p=824</guid>
		<description><![CDATA[来上海百度实习也才没多久，这是第三个礼拜吧，接手的项目相当紧迫，而且作为一个实习生我是main coder，不 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>来上海百度实习也才没多久，这是第三个礼拜吧，接手的项目相当紧迫，而且作为一个实习生我是main coder，不由地带动了极大的工作积极性。这个一开始认为很简单很明了的“小”项目，没想到让我学到了很多东西，今天就讲讲关于Java编码的那些事儿。</p>
<p><span id="more-824"></span></p>
<h3>Java编码</h3>
<p>Unicode是全球标准字符集，是Java所为String采用的编码方式，任何字符用2个字节表示。String实例中保存有一个char[]字符数组。string.getByte()方法可以获得到这个字符串实例在指定编码下的字节数组，<strong>注意</strong>的是不带参数的getByte方法使用OS默认的字符集，比如GB2312（简体中文）。所以要得到Unicode下的字节数组，需要这样：string.getBytes(&#8220;unicode&#8221;)（<strong>此处注意见下文</strong>）。如果使用new String(byte[], Charset)构造，可以将已知编码的字节数组重新拼成一个String实例，即用指定的Charset去组合字节为Unicode字符罢了。同理，不带Charset的String构造使用OS默认字符集。</p>
<p>因此，得到UTF-8的字节数组，按以下步骤：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #003399;">String</span> str <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;梦&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000066; font-weight: bold;">byte</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> bytes <span style="color: #339933;">=</span> str.<span style="color: #006633;">getBytes</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;UTF-8&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>   <span style="color: #0000ff;">&quot;使用UTF-8解码字符串得到的UTF-8字节数组&quot;</span>
<span style="color: #003399;">String</span> str2 <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">String</span><span style="color: #009900;">&#40;</span>bytes, <span style="color: #0000ff;">&quot;utf8&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #0000ff;">&quot;按照当初被解码的方式（utf8）重新组成Java String类&quot;</span>
str.<span style="color: #006633;">equals</span><span style="color: #009900;">&#40;</span>str2<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>       <span style="color: #0000ff;">&quot;使用大小写不同的编码写法，来区别不同API中参数代表的意义&quot;</span></pre></td></tr></table></div>

<p>所以，str = new String(str.getBytes(Charset), Charset) 什么都没有做，除了新建了个String对象。</p>
<p>但是如果你要获取unicode的字节数组，却有非常多的选择，而且很容易出错。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">void</span> echoBytesTest<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #003399;">String</span> s <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;梦&quot;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;Unicode&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UnicodeBig&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UnicodeLittle&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UnicodeBigUnmarked&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UnicodeLittleUnmarked&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UTF-16&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UTF-16BE&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UTF-16LE&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	echoBytes<span style="color: #009900;">&#40;</span>s, <span style="color: #0000ff;">&quot;UTF-8&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000066; font-weight: bold;">void</span> echoBytes<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> s, <span style="color: #003399;">String</span> encoding<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000066; font-weight: bold;">byte</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> bytes <span style="color: #339933;">=</span> s.<span style="color: #006633;">getBytes</span><span style="color: #009900;">&#40;</span>encoding<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">byte</span> b <span style="color: #339933;">:</span> bytes<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> b <span style="color: #339933;">&amp;</span> 0xff<span style="color: #339933;">;</span>
		<span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">print</span><span style="color: #009900;">&#40;</span><span style="color: #003399;">Integer</span>.<span style="color: #006633;">toHexString</span><span style="color: #009900;">&#40;</span>i<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> <span style="color: #0000ff;">&quot; &quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</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: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>JDK6.0环境下的输出结果如下所示：</p>
<table>
<tbody>
<tr>
<td>Unicode</td>
<td><font color="red">fe ff</font> 68 a6</td>
<td>BE顺序，带BOM</td>
<td>强烈申明不要使用，<strong>JDK相关</strong></td>
</tr>
<tr>
<td>UnicodeBig</td>
<td><font color="red">fe ff</font> 68 a6</td>
<td>BE顺序，带BOM</td>
<td><font color="blue">推荐使用</font></td>
</tr>
<tr>
<td>UnicodeLittle</td>
<td><font color="red">ff fe</font> a6 68</td>
<td>LE顺序，带BOM</td>
<td><font color="blue">推荐使用</font></td>
</tr>
<tr>
<td>UnicodeBigUnmarked</td>
<td>68 a6</td>
<td>BE顺序，无BOM</td>
<td><font color="blue">推荐使用</font></td>
</tr>
<tr>
<td>UnicodeLittleUnmarked</td>
<td>a6 68</td>
<td>LE顺序，无BOM</td>
<td><font color="blue">推荐使用</font></td>
</tr>
<tr>
<td>UTF-16</td>
<td><font color="red">fe ff</font> 68 a6</td>
<td>BE顺序，带BOM</td>
<td>不推荐使用，可读性不够高</td>
</tr>
<tr>
<td>UTF-16BE</td>
<td>68 a6</td>
<td>BE顺序，无BOM</td>
<td>不推荐使用，可读性不够高</td>
</tr>
<tr>
<td>UTF-16LE</td>
<td>a6 68</td>
<td>LE顺序，无BOM</td>
<td>不推荐使用，可读性不够高</td>
</tr>
<tr>
<td>UTF-8</td>
<td>e6 a2 a6</td>
<td>无BOM</td>
<td>此为UTF-8字节串，特以此区别</td>
</tr>
</tbody>
</table>
<p><strong>然后要特别说明的是，getBytes(&#8220;Unicode&#8221;)这种方式是什么样的结果，却是与JDK相关的</strong>，JDK5.0使用LE顺序，JDK6.0使用BE顺序！</p>
<p>有点复杂，看官方解释。</p>
<blockquote><p>
为了在读取字节时能知道所采用的字节序，在传输时采用了一个名为 “Zero Width No-Break Space” 的字符（中文译名作“零宽无间断间隔”）用于限定字节序，开头两个字节为 FE FF（即-2 -1） 时为 Big-Endian，为 FF FE（即-1 -2） 时为 Little-Endian。 详见 RFC2781 3.2 节。这个就是传说中的BOM头。<br />
UTF-8不需要 BOM 来表明字节顺序，但可以用 BOM 来表明编码方式。字符 &#8220;Zero Width No-Break Space&#8221; 的 UTF-8 编码是 EF BB BF。<br />
详见：<a href="http://baike.baidu.com/view/126558.htm#sub5073178" target="_blank">BOM_百度百科</a>
</p></blockquote>
<p>自我理解很像体系结构中的大端编址和小端编址对吧。不知道Unicode为什么要存在Big-Endian和Little-Endian两种顺序，反正就是存在着呗。</p>
<h3>说说项目</h3>
<p>就是因为string.getBytes(&#8220;Unicode&#8221;)的JDK相关性，然后我因为使用了旧版本的JDK导致和大家同样从SVN签出的代码的运行结果却完全不一致，由于完全信任Java的平台无关，没有一点怀疑是JDK源码的原因，整整花了一天的时候排查bug。</p>
<p>第二个是Spring MVC的@ResponseBody默认返回的字符集是ISO-8859-1的西文字符，导致返回客户端为乱码，修改Spring配置的见<a href="http://forum.springsource.org/showthread.php?t=81858" target="_blank">Spring官方论坛的解决方案</a>，不过貌似试了几种方法都没有起效&#8230;其实最简单的方式就是放弃使用Spring提供的Ajax方式，直接使用HttpServletResponse的getWriter().write()方法写字符串或getOutputStream.write()方法直接写字节。</p>
<h3>参考引用</h3>
<p>百度百科：<a href="http://baike.baidu.com/view/126558.htm#sub5073178">http://baike.baidu.com/view/126558.htm#sub5073178</a></p>
<p>CSDN论坛：<a href="http://topic.csdn.net/u/20081009/09/e899898c-591f-4985-ae88-5972475708fb.html" target="_blank">http://topic.csdn.net/u/20081009/09/e899898c-591f-4985-ae88-5972475708fb.html</a></p>
<p>Spring官方论坛：<a href="http://forum.springsource.org/showthread.php?t=81858">http://forum.springsource.org/showthread.php?t=81858</a></p>
<h4  class="related_post_title">看看 Ajax , Spring , 编码</h4><ul class="related_post"><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>2014-06-17 -- <a target="_blank" href="http://blog.11034.org/2014-06/onchange_of_input.html" title="小心使用input的onchange事件">小心使用input的onchange事件</a></li><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-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/2011-03/java_encoding.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
