﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-细心!用心!耐心!-文章分类-多线程编程</title><link>http://www.blogjava.net/jesson2005/category/19580.html</link><description>吾非文人，乃市井一俗人也，读百卷书，跨江河千里，故申城一游； 
一两滴辛酸，三四年学业，五六点粗墨，七八笔买卖，九十道人情。</description><language>zh-cn</language><lastBuildDate>Mon, 26 May 2008 14:36:43 GMT</lastBuildDate><pubDate>Mon, 26 May 2008 14:36:43 GMT</pubDate><ttl>60</ttl><item><title>线程池的实现</title><link>http://www.blogjava.net/jesson2005/articles/200958.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Fri, 16 May 2008 09:07:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/200958.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/200958.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/200958.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/200958.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/200958.html</trackback:ping><description><![CDATA[线程池也许很多比较陌生 但是提到servlet每个人都知道，servlet就是用线程池来处理请求的 <br />
一个线程池由线程池管理器 工作线程 任务队列和任务接口组成 <br />
<strong>一 线程池管理器</strong>---ThreadPoolMananger 主要负责启动 停止工作线程
<ol class="dp-j">
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;ThreadPoolManager&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;DEFAULT_POOL_SIZE&nbsp;=&nbsp;</span><span class="number">4</span><span>; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;List&lt;WorkThread&gt;&nbsp;threadPool; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;Queue&lt;Task&gt;&nbsp;taskQueue; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;poolSize; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;ThreadPoolManager(){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>(DEFAULT_POOL_SIZE); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;ThreadPoolManager(</span><span class="keyword">int</span><span>&nbsp;poolSize){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(poolSize&nbsp;&lt;=&nbsp;</span><span class="number">0</span><span>){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.poolSize&nbsp;=&nbsp;DEFAULT_POOL_SIZE; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span class="keyword">else</span><span>{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.poolSize&nbsp;=&nbsp;poolSize; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadPool&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;ArrayList&lt;WorkThread&gt;(</span><span class="keyword">this</span><span>.poolSize); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskQueue&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;ConcurrentLinkedQueue&lt;Task&gt;(); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startup(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/** </span>&nbsp;</span></li>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;启动线程池&nbsp;开始处理任务 </span>&nbsp;</span></li>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;startup(){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"启动工作线程。。。"</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">synchronized</span><span>(taskQueue){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;i&nbsp;&lt;&nbsp;DEFAULT_POOL_SIZE;&nbsp;i++){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkThread&nbsp;workThread&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;WorkThread(taskQueue); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadPool.add(&nbsp;workThread&nbsp;); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;workThread.start(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/** </span>&nbsp;</span></li>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;停止工作线程。工作线程不一定立即停止，只有在线程处于运行状态时会立即停止 </span>&nbsp;</span></li>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;shutdown(){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"停止工作线程."</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">synchronized</span><span>(taskQueue){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;i&nbsp;&lt;&nbsp;DEFAULT_POOL_SIZE;&nbsp;i++){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threadPool.get(i).shutdown(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/** </span>&nbsp;</span></li>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;添加消息到队尾,&nbsp;&nbsp; </span>&nbsp;</span></li>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;addTask(Task&nbsp;task){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">synchronized</span><span>(taskQueue){ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskQueue.add(task); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskQueue.notifyAll();&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
<p><span><strong>二 工作线程---WorkerThread</strong> 顾名思义 它本身就是一个线程，而且是专门用来工作的，工作线程的主要任务是从任务队列中取出任务 然后执行任务 <br />
</p>
<li><span><span class="comment">/** </span>&nbsp;</span></li>
<li><span><span class="comment">&nbsp;*&nbsp;工作线程 </span>&nbsp;</span></li>
<li><span><span class="comment">&nbsp;*&nbsp;@author&nbsp;XuLiangYong </span>&nbsp;</span></li>
<li><span><span class="comment">&nbsp;*&nbsp;Jul&nbsp;20,&nbsp;2007&nbsp;3:47:52&nbsp;PM </span>&nbsp;</span></li>
<li><span><span class="comment">&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
<li><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;WorkThread&nbsp;</span><span class="keyword">extends</span><span>&nbsp;Thread{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;shutdown&nbsp;=&nbsp;</span><span class="keyword">false</span><span>; &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;Queue&lt;Task&gt;&nbsp;queue; &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;WorkThread(Queue&lt;Task&gt;&nbsp;queue){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.queue&nbsp;=&nbsp;queue; &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;run(){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>(!shutdown){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">synchronized</span><span>(queue){&nbsp;</span><span class="comment">//获得对象锁&nbsp;禁止其他线程访问 </span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!queue.isEmpty()){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//处理任务&nbsp; </span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Task&nbsp;task&nbsp;=&nbsp;queue.poll(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task.execute(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span class="keyword">else</span><span>{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;queue.wait();&nbsp;</span><span class="comment">//释放锁&nbsp;线程处于阻赛状态&nbsp;等待notify唤醒 </span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">catch</span><span>&nbsp;(InterruptedException&nbsp;e)&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span class="comment">//end&nbsp;while </span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/** </span>&nbsp;</span></li>
<li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;调用该方法后不一定会立即结束线程，&nbsp;只有在线程处于运行状态且处理完当前任务后才结束 </span>&nbsp;</span></li>
<li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;shutdown(){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shutdown&nbsp;=&nbsp;</span><span class="keyword">true</span><span>; &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;&nbsp;</span></li>
<p>&nbsp;</p>
<p><strong>三 任务队列---TaskQueue</strong> FIFO数据结构 在出对入队的时候要<strong>锁定对象</strong>避免两个线程重复处理某任务 <br />
在这里我采用的是java提供的ConcurrentLinkedQueue队列，这是一个用链表实现的队 可无限的扩大，具体用法请看doc <br />
用到队列的地方主要有两个 addTask(Task task) 和 Task task = queue.poll(); <br />
<br />
<br />
<strong>四 任务接口---Task</strong> 任务接口只有一个方法 execute（），使用者只需实现这个接口就可以了 </p>
<li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">interface</span><span>&nbsp;Task&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;execute(); &nbsp;&nbsp;</span></span></li>
<li><span>}</span></li>
<p><br />
<br />
用法：</p>
<li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;TestThreadPoolManager&nbsp;</span><span class="keyword">extends</span><span>&nbsp;TestCase&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;test(){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ThreadPoolManager&nbsp;pool&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;ThreadPoolManager(); &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;i&nbsp;&lt;&nbsp;</span><span class="number">100</span><span>;&nbsp;i++){ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.addTask(</span><span class="keyword">new</span><span>&nbsp;SimpleTask(</span><span class="keyword">new</span><span>&nbsp;MyManager(),&nbsp;i));&nbsp;</span><span class="comment">//SimpleTask实现了Task接口 </span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.shutdown(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;</span></li>
<p>可以看出用户的使用非常简单 <br />
在jdk5中 java提供了线程池<br />
有一点注意 千万不要在servlet中调用线程池 因为servlet本来就是一个线程池 <br />
<br />
<br />
</span></p>
<img src ="http://www.blogjava.net/jesson2005/aggbug/200958.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2008-05-16 17:07 <a href="http://www.blogjava.net/jesson2005/articles/200958.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线程池的介绍及简单实现</title><link>http://www.blogjava.net/jesson2005/articles/96934.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Wed, 31 Jan 2007 05:57:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/96934.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/96934.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/96934.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/96934.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/96934.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr valign="top">
								<td width="100%">
										<h1 align="center">线程池的介绍及简单实现</h1>
										<img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" />
								</td>
								<td class="no-print" width="192">
								</td>
						</tr>
				</tbody>
		</table>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr valign="top">
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td width="100%">
										<p>服务器程序利用线程技术响应客户请求已经司空见惯,可能您认为这样做效率已经很高，但您有没有想过优化一下使用线程的方法。该文章将向您介绍服务器程序如何利用线程池来优化性能并提供一个简单的线程池实现。</p>
										<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
										<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
										<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
										<p>
												<a name="1">
														<span class="atitle">
																<font face="Arial" size="4">线程池的技术背景</font>
														</span>
												</a>
										</p>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
										<p>在面向对象编程中，创建和销毁对象是很费时间的，因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此，虚拟机将试图跟踪每一个对象，以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数，特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题，其实这就是一些"池化资源"技术产生的原因。比如大家所熟悉的数据库连接池正是遵循这一思想而产生的，本文将介绍的线程池技术同样符合这一思想。</p>
										<p>目前，一些著名的大公司都特别看好这项技术，并早已经在他们的产品中应用该技术。比如IBM的WebSphere，IONA的Orbix 2000在SUN的 Jini中，Microsoft的MTS（Microsoft Transaction Server 2.0），COM+等。</p>
										<p>现在您是否也想在服务器程序应用该项技术?</p>
										<br />
										<p>
												<a name="2">
														<span class="atitle">
																<font face="Arial" size="4">线程池技术如何提高服务器程序的性能</font>
														</span>
												</a>
										</p>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
										<p>我所提到服务器程序是指能够接受客户请求并能处理请求的程序，而不只是指那些接受网络客户请求的网络服务器程序。</p>
										<p>多线程技术主要解决处理器单元内多个线程执行的问题，它可以显著减少处理器单元的闲置时间，增加处理器单元的吞吐能力。但如果对多线程应用不当，会增加对单个任务的处理时间。可以举一个简单的例子：</p>
										<p>假设在一台服务器完成一项任务的时间为T</p>
										<pre>     T1 创建线程的时间
      T2 在线程中执行任务的时间，包括线程间同步所需时间
      T3 线程销毁的时间		</pre>
										<p>显然T ＝ T1＋T2＋T3。注意这是一个极度简化的假设。</p>
										<p>可以看出T1,T3是多线程本身的带来的开销，我们渴望减少T1,T3所用的时间，从而减少T的时间。但一些线程的使用者并没有注意到这一点，所以在程序中频繁的创建或销毁线程，这导致T1和T3在T中占有相当比例。显然这是突出了线程的弱点（T1，T3），而不是优点（并发性）。</p>
										<p>线程池技术正是关注如何缩短或调整T1,T3时间的技术，从而提高服务器程序性能的。它把T1，T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段，这样在服务器程序处理客户请求时，不会有T1，T3的开销了。</p>
										<p>线程池不仅调整T1,T3产生的时间段，而且它还显著减少了创建线程的数目。在看一个例子：</p>
										<p>假设一个服务器一天要处理50000个请求，并且每个请求需要一个单独的线程完成。我们比较利用线程池技术和不利于线程池技术的服务器处理这些请求时所产生的线程总数。在线程池中，线程数一般是固定的，所以产生线程总数不会超过线程池中线程的数目或者上限（以下简称线程池尺寸），而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池尺寸是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间，从而提高效率。</p>
										<p>这些都是假设，不能充分说明问题，下面我将讨论线程池的简单实现并对该程序进行对比测试，以说明线程技术优点及应用领域。</p>
										<br />
										<br />
										<p>
												<a name="3">
														<span class="atitle">
																<font face="Arial" size="4">线程池的简单实现及对比测试</font>
														</span>
												</a>
										</p>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
										<p>一般一个简单线程池至少包含下列组成部分。</p>
										<ol>
												<li>线程池管理器（ThreadPoolManager）:用于创建并管理线程池 
</li>
												<li>工作线程（WorkThread）: 线程池中线程 
</li>
												<li>任务接口（Task）:每个任务必须实现的接口，以供工作线程调度任务的执行。 
</li>
												<li>任务队列:用于存放没有处理的任务。提供一种缓冲机制。 </li>
										</ol>
										<p>线程池管理器至少有下列功能：创建线程池，销毁线程池，添加新任务创建线程池的部分代码如下：</p>
										<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
												<tbody>
														<tr>
																<td>
																		<pre>
																				<code class="section">
																						<font face="Lucida Console">  …
        //create threads
        synchronized(workThreadVector)
        {
            for(int j = 0; j &lt; i; j++)
            {
                threadNum++;
               WorkThread workThread = new WorkThread(taskVector, threadNum);
                workThreadVector.addElement(workThread);
            }

        }
…
</font>
																				</code>
																		</pre>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<p>注意同步workThreadVector并没有降低效率，相反提高了效率,请参考Brian Goetz的文章。 销毁线程池的部分代码如下：</p>
										<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
												<tbody>
														<tr>
																<td>
																		<pre>
																				<code class="section">
																						<font face="Lucida Console">  …
        while(!workThreadVector.isEmpty())
        {
        if(debugLevel &gt; 2)
         System.out.println("stop:"+(i));
         i++;
            try
            {
                WorkThread workThread = (WorkThread)workThreadVector.remove(0);
                workThread.closeThread();
                continue;
            }
            catch(Exception exception)
            {
                if(debugLevel &gt; 2)
                    exception.printStackTrace();
            }
            break;
        }
   …
   </font>
																				</code>
																		</pre>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<p>添加新任务的部分代码如下：</p>
										<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
												<tbody>
														<tr>
																<td>
																		<pre>
																				<code class="section">
																						<font face="Lucida Console">   …
        synchronized(taskVector)
        {
            taskVector.addElement(taskObj);
            taskVector.notifyAll();
        }
   …
   </font>
																				</code>
																		</pre>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<p>工作线程是一个可以循环执行任务的线程，在没有任务时将等待。由于代码比较多在此不罗列.</p>
										<p>任务接口是为所有任务提供统一的接口，以便工作线程处理。任务接口主要规定了任务的入口，任务执行完后的收尾工作，任务的执行状态等。在文章结尾有相关代码的下载。</p>
										<p>以上所描述的线程池结构很简单，一些复杂的线程池结构将不再此讨论。</p>
										<p>在下载代码中有测试驱动程序（TestThreadPool），我利用这个测试程序的输出数据统计出下列测试结果。测试有两个参数要设置：</p>
										<ol>
												<li>线程池中线程数，即线程池尺寸。 
</li>
												<li>要完成的任务数。 </li>
										</ol>
										<p>分别将一个参数固定，另一个参数变动以考察两个参数所产生的不同结果。所用测试机器分别为普通PC机（Win2000 JDK1.3.1）和SUN服务器(Solaris Unix JDK1.3.1)，机器配置在此不便指明。</p>
										<p>
												<a name="N100B0">
														<span class="smalltitle">
																<strong>
																		<font face="Arial">表1:测试数据及对应结果</font>
																</strong>
														</span>
												</a>
										</p>
										<p>
										</p>
										<table width="80%" border="1">
												<tbody>
														<tr bgcolor="#c0c0c0">
																<td>线程池尺寸</td>
																<td>任务数</td>
																<td>没有应用线程池所用的时间（单位:毫秒，OS:win）</td>
																<td>应用线程池所用的时间（单位:毫秒，OS:win）</td>
																<td>没有应用线程池所用的时间（单位:毫秒，OS:Solaris）</td>
																<td>应用线程池所用的时间（单位:毫秒，OS:Solaris）</td>
														</tr>
														<tr>
																<td>1</td>
																<td>5000</td>
																<td>3896</td>
																<td>130</td>
																<td>6513</td>
																<td>327</td>
														</tr>
														<tr>
																<td>2</td>
																<td>5000</td>
																<td>3455</td>
																<td>151</td>
																<td>6221</td>
																<td>659</td>
														</tr>
														<tr>
																<td>4</td>
																<td>5000</td>
																<td>3425</td>
																<td>120</td>
																<td>5448</td>
																<td>433</td>
														</tr>
														<tr>
																<td>8</td>
																<td>5000</td>
																<td>3475</td>
																<td>160</td>
																<td>5769</td>
																<td>1478</td>
														</tr>
														<tr>
																<td>16</td>
																<td>5000</td>
																<td>3505</td>
																<td>211</td>
																<td>5785</td>
																<td>1970</td>
														</tr>
														<tr>
																<td>32</td>
																<td>5000</td>
																<td>3455</td>
																<td>251</td>
																<td>6403</td>
																<td>875</td>
														</tr>
														<tr>
																<td>64</td>
																<td>5000</td>
																<td>3595</td>
																<td>501</td>
																<td>5182</td>
																<td>1103</td>
														</tr>
														<tr>
																<td>128</td>
																<td>5000</td>
																<td>3515</td>
																<td>881</td>
																<td>5154</td>
																<td>405</td>
														</tr>
														<tr>
																<td>256</td>
																<td>5000</td>
																<td>3495</td>
																<td>3104</td>
																<td>5502</td>
																<td>1589</td>
														</tr>
														<tr>
																<td>512</td>
																<td>5000</td>
																<td>3425</td>
																<td>5488</td>
																<td>5667</td>
																<td>1262</td>
														</tr>
														<tr>
																<td>16</td>
																<td>1</td>
																<td>20</td>
																<td>0</td>
																<td>22</td>
																<td>3</td>
														</tr>
														<tr>
																<td>16</td>
																<td>2</td>
																<td>20</td>
																<td>20</td>
																<td>21</td>
																<td>13</td>
														</tr>
														<tr>
																<td>16</td>
																<td>4</td>
																<td>20</td>
																<td>10</td>
																<td>27</td>
																<td>10</td>
														</tr>
														<tr>
																<td>16</td>
																<td>8</td>
																<td>20</td>
																<td>20</td>
																<td>22</td>
																<td>24</td>
														</tr>
														<tr>
																<td>16</td>
																<td>16</td>
																<td>30</td>
																<td>20</td>
																<td>29</td>
																<td>48</td>
														</tr>
														<tr>
																<td>16</td>
																<td>32</td>
																<td>40</td>
																<td>20</td>
																<td>46</td>
																<td>108</td>
														</tr>
														<tr>
																<td>16</td>
																<td>64</td>
																<td>60</td>
																<td>20</td>
																<td>72</td>
																<td>199</td>
														</tr>
														<tr>
																<td>16</td>
																<td>128</td>
																<td>110</td>
																<td>20</td>
																<td>148</td>
																<td>335</td>
														</tr>
														<tr>
																<td>16</td>
																<td>256</td>
																<td>201</td>
																<td>20</td>
																<td>252</td>
																<td>132</td>
														</tr>
														<tr>
																<td>16</td>
																<td>512</td>
																<td>411</td>
																<td>40</td>
																<td>522</td>
																<td>382</td>
														</tr>
														<tr>
																<td>16</td>
																<td>1024</td>
																<td>811</td>
																<td>71</td>
																<td>1233</td>
																<td>610</td>
														</tr>
														<tr>
																<td>16</td>
																<td>2048</td>
																<td>1552</td>
																<td>80</td>
																<td>2045</td>
																<td>135</td>
														</tr>
														<tr>
																<td>16</td>
																<td>4096</td>
																<td>2874</td>
																<td>250</td>
																<td>4828</td>
																<td>787</td>
														</tr>
												</tbody>
										</table>
										<br />
										<a name="N102B9">
												<b>图1.线程池的尺寸的对服务器程序的性能影响</b>
										</a>
										<br />
										<img height="372" alt="图1.线程池的尺寸的对服务器程序的性能影响" src="http://www-128.ibm.com/developerworks/cn/java/l-threadPool/ThreadPoolTest1.gif" width="596" />
										<br />
										<p>根据以上统计数据可得出下图：</p>
										<br />
										<a name="N102CB">
												<b>图2.任务数对服务器程序的冲击 </b>
										</a>
										<br />
										<img height="373" alt="图2.任务数对服务器程序的冲击" src="http://www-128.ibm.com/developerworks/cn/java/l-threadPool/ThreadPoolTest2.gif" width="594" />
										<br />
										<p>数据分析如下：</p>
										<p>图1是改变线程池尺寸对服务器性能的影响，在该测试过程中，服务器的要完成的任务数固定为为5000。从图1中可以看出合理配置线程池尺寸对于大量任务处理的效率有非常明显的提高，但是一旦尺寸选择不合理（过大或过小）就会严重降低影响服务器性能。理论上"过小"将出现任务不能及时处理的情况,但在图表中显示出某些小尺寸的线程池表现很好，这是因为测试驱动中有很多线程同步开销，且这个开销相对于完成单个任务的时间是不能忽略的。"过大"则会出现线程间同步开销太大的问题，而且在线程间切换很耗CPU时间，在图表显示的很清楚。可见任何一个好技术，如果滥用都会造成灾难性后果。</p>
										<p>图2是用不同数量的任务来冲击服务器程序，在该测试过程中，服务器线程池尺寸固定为16。可以看出线程池在处理少量任务时的优势不明显。所以线程池技术有一定的适应范围，关于适用范围将在后面讨论。但对于大量的任务的处理，线程池的优势表现非常卓越，服务器程序处理请求的时间虽然有波动，但是其平均值相对小多了。</p>
										<p>值得注意的是测试方案中，统计任务的完成时间没有包含了创建线程池的时间。在实际线程池工作时，即利用线程池处理任务时，创建线程池的时间是不必计算在内的。</p>
										<p>由于测试驱动程序有很多同步代码，特别是等待线程执行完毕的同步（代码中为sleepToWait(long l)方法的调用），这些代码降低了代码执行效率，这是测试驱动一个缺点，但这个测试驱动可以说明线程池相对于简单使用线程的优势。</p>
										<br />
										<br />
										<p>
												<a name="4">
														<span class="atitle">
																<font face="Arial" size="4">关于高级线程池的探讨</font>
														</span>
												</a>
										</p>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
										<p>简单线程池存在一些问题，比如如果有大量的客户要求服务器为其服务，但由于线程池的工作线程是有限的，服务器只能为部分客户服务，其它客户提交的任务，只能在任务队列中等待处理。一些系统设计人员可能会不满这种状况，因为他们对服务器程序的响应时间要求比较严格，所以在系统设计时可能会怀疑线程池技术的可行性，但是线程池有相应的解决方案。调整优化线程池尺寸是高级线程池要解决的一个问题。主要有下列解决方案：</p>
										<p>
												<a name="N102F2">
														<span class="smalltitle">
																<strong>
																		<font face="Arial">方案一：动态增加工作线程</font>
																</strong>
														</span>
												</a>
										</p>
										<p>
												<strong>
														<font face="Arial">
														</font>
												</strong>
										</p>
										<p>在一些高级线程池中一般提供一个可以动态改变的工作线程数目的功能，以适应突发性的请求。一旦请求变少了将逐步减少线程池中工作线程的数目。当然线程增加可以采用一种超前方式，即批量增加一批工作线程，而不是来一个请求才建立创建一个线程。批量创建是更加有效的方式。该方案还有应该限制线程池中工作线程数目的上限和下限。否则这种灵活的方式也就变成一种错误的方式或者灾难，因为频繁的创建线程或者短时间内产生大量的线程将会背离使用线程池原始初衷--减少创建线程的次数。</p>
										<p>举例：Jini中的TaskManager，就是一个精巧线程池管理器，它是动态增加工作线程的。SQL Server采用单进程(Single Process)多线程(Multi-Thread)的系统结构，1024个数量的线程池，动态线程分配，理论上限32767。</p>
										<p>
												<a name="N10300">
														<span class="smalltitle">
																<strong>
																		<font face="Arial">方案二：优化工作线程数目</font>
																</strong>
														</span>
												</a>
										</p>
										<p>
												<strong>
														<font face="Arial">
														</font>
												</strong>
										</p>
										<p>如果不想在线程池应用复杂的策略来保证工作线程数满足应用的要求，你就要根据统计学的原理来统计客户的请求数目，比如高峰时段平均一秒钟内有多少任务要求处理，并根据系统的承受能力及客户的忍受能力来平衡估计一个合理的线程池尺寸。线程池的尺寸确实很难确定，所以有时干脆用经验值。</p>
										<p>举例：在MTS中线程池的尺寸固定为100。</p>
										<p>
												<a name="N1030E">
														<span class="smalltitle">
																<strong>
																		<font face="Arial">方案三：一个服务器提供多个线程池</font>
																</strong>
														</span>
												</a>
										</p>
										<p>
												<strong>
														<font face="Arial">
														</font>
												</strong>
										</p>
										<p>在一些复杂的系统结构会采用这个方案。这样可以根据不同任务或者任务优先级来采用不同线程池处理。</p>
										<p>举例：COM+用到了多个线程池。</p>
										<p>这三种方案各有优缺点。在不同应用中可能采用不同的方案或者干脆组合这三种方案来解决实际问题。</p>
										<br />
										<br />
										<p>
												<a name="5">
														<span class="atitle">
																<font face="Arial" size="4">线程池技术适用范围及应注意的问题</font>
														</span>
												</a>
										</p>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
										<p>下面是我总结的一些线程池应用范围,可能是不全面的。</p>
										<p>线程池的应用范围：</p>
										<ol>
												<li>需要大量的线程来完成任务，且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务，使用线程池技术是非常合适的。因为单个任务小，而任务数量巨大，你可以想象一个热门网站的点击次数。 但对于长时间的任务，比如一个Telnet连接请求，线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。 
</li>
												<li>对性能要求苛刻的应用，比如要求服务器迅速相应客户请求。 
</li>
												<li>接受突发性的大量请求，但不至于使服务器因此产生大量线程的应用。突发性大量客户请求，在没有线程池情况下，将产生大量线程，虽然理论上大部分操作系统线程数目最大值不是问题，短时间内产生大量线程可能使内存到达极限，并出现"OutOfMemory"的错误。 </li>
										</ol>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
										<p>本文只是简单介绍线程池技术。可以看出线程池技术对于服务器程序的性能改善是显著的。线程池技术在服务器领域有着广泛的应用前景。希望这项技术能够应用到您的多线程服务程序中。</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/jesson2005/aggbug/96934.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-01-31 13:57 <a href="http://www.blogjava.net/jesson2005/articles/96934.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA后台程序设计及UTIL.CONCURRENT包的应用</title><link>http://www.blogjava.net/jesson2005/articles/96927.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Wed, 31 Jan 2007 05:52:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/96927.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/96927.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/96927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/96927.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/96927.html</trackback:ping><description><![CDATA[
		<h2 class="title_entry" align="center">JAVA后台程序设计及UTIL.CONCURRENT包的应用</h2>
		<div class="p_entry2">
				<font color="blue">JAVA后台程序设计及UTIL.CONCURRENT包的应用<br /><br />何 恐<br /><br />摘要 : 在很多软件项目中，JAVA语言常常被用来开发后台服务程序。线程池技术是提高这类程序性能的一个重要手段。在实践中，该技术已经被广泛的使用。本文首先 对设计后台服务程序通常需要考虑的问题进行了基本的论述，随后介绍了JAVA线程池的原理、使用和其他一些相关问题，最后对功能强大的JAVA开放源码线 程池包util.concurrent 在实际编程中的应用进行了详细介绍。<br />关键字: JAVA；线程池；后台服务程序；util.concurrent<br /><br /><br />1 引言<br />在软件项目开发中，许多后台服务程序的处理动作流程都具有一个相同点，就是：接受客户端发来的请求，对请求进行一些相关的处理，最后将处理结果返回给客户 端。这些请求的来源和方式可能会各不相同，但是它们常常都有一个共同点：数量巨大，处理时间短。这类服务器在实际应用中具有较大的普遍性，如web服务 器，短信服务器，DNS服务器等等。因此，研究如何提高此类后台程序的性能，如何保证服务器的稳定性以及安全性都具有重要的实用价值。<br /><br />2 后台服务程序设计<br />2.1 关于设计原型<br />构建服务器应用程序的一个简单的模型是：启动一个无限循环，循环里放一个监听线程监听某个地址端口。每当一个请求到达就创建一个新线程，然后新线程为请求服务，监听线程返回继续监听。<br />简单举例如下：<br />import java.net.*;<br />public class MyServer extends Thread{<br />public void run(){<br />try{<br />ServerSocket server=null;<br />Socket clientconnection=null;<br />server = new ServerSocket(8008);//监听某地址端口对<br />while(true){进入无限循环<br />clientconnection =server.accept();//收取请求<br />new ServeRequest(clientconnection).start();//启动一个新服务线程进行服务<br />……<br />}<br />}catch(Exception e){<br />System.err.println("Unable to start serve listen:"+e.getMessage());<br />e.printStackTrace();<br />}<br />}<br />}<br />实际上，这只是个简单的原型，如果试图部署以这种方式运行的服务器应用程序，那么这种方法的严重不足就很明显。<br />首先，为每个请求创建一个新线程的开销很大，为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源, 往往有时候要比花在处理实际的用户请求的时间和资源更多。在Java中更是如此，虚拟机将试图跟踪每一个对象，以便能够在对象销毁后进行垃圾回收。所以提 高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数。这样综合看来，系统的性能瓶颈就在于线程的创建开销。<br />其次，除了创建和销毁线程的开销之外，活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足，服务器应用程序需要一些办法来限制任何给定时刻运行的处理 线程数目，以防止服务器被“压死”的情况发生。所以在设计后台程序的时候，一般需要提前根据服务器的内存、CPU等硬件情况设定一个线程数量的上限值。<br />如果创建和销毁线程的时间相对于服务时间占用的比例较大，那末假设在一个较短的时间内有成千上万的请求到达，想象一下，服务器的时间和资源将会大量的花在 创建和销毁线程上，而真正用于处理请求的时间却相对较少，这种情况下，服务器性能瓶颈就在于创建和销毁线程的时间。按照这个模型写一个简单的程序测试一下 即可看出，由于篇幅关系，此处略。如果把（服务时间/创建和销毁线程的时间）作为衡量服务器性能的一个参数，那末这个比值越大，服务器的性能就越高。<br />应此，解决此类问题的实质就是尽量减少创建和销毁线程的时间，把服务器的资源尽可能多地用到处理请求上来，从而发挥多线程的优点（并发），避免多线程的缺点（创建和销毁的时空开销）。<br />线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程，线程创建的开销被分摊到了多个任务上。其好处是，因为在请求到达时 线程已经存在，所以无意中也消除了线程创建所带来的延迟。这样，就可以立即为请求服务，使应用程序响应更快。而且，通过适当地调整线程池中的线程数目，也 就是当请求的数目超过某个阈值时，就强制其它任何新到的请求一直等待，直到获得一个线程来处理为止，从而可以防止资源不足。<br /><br />3　　　　JAVA线程池原理<br />3.1 原理以及实现<br />在实践中，关于线程池的实现常常有不同的方法，但是它们的基本思路大都是相似的：服务器预先存放一定数目的“热”的线程，并发程序需要使用线程的时候，从 服务器取用一条已经创建好的线程（如果线程池为空则等待），使用该线程对请求服务，使用结束后，该线程并不删除，而是返回线程池中，以备复用，这样可以避 免对每一个请求都生成和删除线程的昂贵操作。<br />一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器（ThreadPool Manager）的作用是创建、销毁并管理线程池，将工作线程放入线程池中；工作线程是一个可以循环执行任务的线程，在没有任务时进行等待；任务队列的作 用是提供一种缓冲机制，将没有处理的任务放在任务队列中；任务接口是每个任务必须实现的接口，主要用来规定任务的入口、任务执行完后的收尾工作、任务的执 行状态等，工作线程通过该接口调度任务的执行。下面的代码实现了创建一个线程池：<br />public class ThreadPool<br />{ <br />private Stack threadpool = new Stack();<br />private int poolSize;<br />private int currSize=0;<br />public void setSize(int n)<br />{ <br />poolSize = n;<br />}<br />public void run()<br />{<br />for(int i=0;i</font>
				<br />
				<em>(发帖时间:2003-11-30 11:55:56)</em>
				<br />
				<a title="发送传呼" onclick="return SendMsg(this.href);" href="http://www.cxylm.com/asp/us/msg/umsg_send.asp?usid=200306120835351868">---</a>
				<span style="BACKGROUND-COLOR: rgb(232,232,232)">
						<a title="修改发言" href="http://www.cxylm.com/asp/bbs/public/bp_edittop.asp?t_id=200311301155569497&amp;b_id=9099">岑心</a>
				</span>
				<a title="察看用户资料" onclick="return ViewUser(this.href);" href="http://www.cxylm.com/asp/us/info/uinfo_show.asp?user_id=200306120835351868">
						<font face="Wingdings" size="4">J</font>
				</a>
		</div>
		<p class="p_entry2">回复(1): <font title="2003-11-30 11:56:28"><font color="blue"><br /><br />4.2　　　　框架与结构<br />下面让我们来看看util.concurrent的框架结构。关于这个工具包概述的e文原版链接地址是http: //gee.cs.oswego.edu/dl/cpjslides/util.pdf。该工具包主要包括三大部分：同步、通道和线程池执行器。第一部分 主要是用来定制锁，资源管理，其他的同步用途；通道则主要是为缓冲和队列服务的；线程池执行器则提供了一组完善的复杂的线程池实现。<br />--主要的结构如下图所示<br /><br />4.2.1 Sync<br />acquire/release协议的主要接口<br />- 用来定制锁，资源管理，其他的同步用途<br />- 高层抽象接口<br />- 没有区分不同的加锁用法<br /><br />实现<br />-Mutex, ReentrantLock, Latch, CountDown,Semaphore, WaiterPreferenceSemaphore, FIFOSemaphore, PrioritySemaphore<br />还有，有几个简单的实现，例如ObservableSync, LayeredSync<br /><br />举例：如果我们要在程序中获得一独占锁，可以用如下简单方式：<br />try {<br />lock.acquire();<br />try {<br />action();<br />}<br />finally {<br />lock.release();<br />}<br />}catch（Exception e）{<br />}<br /><br />程序中,使用lock对象的acquire()方法获得一独占锁，然后执行您的操作，锁用完后，使用release()方法释放之即可。呵呵，简单吧，想 想看，如果您亲自撰写独占锁，大概会考虑到哪些问题？如果关键的锁得不到怎末办？用起来是不是会复杂很多？而现在，以往的很多细节和特殊异常情况在这里都 无需多考虑，您尽可以把精力花在解决您的应用问题上去。<br /><br />4.2.2 通道(Channel)<br />为缓冲，队列等服务的主接口<br /><br />具体实现<br />LinkedQueue, BoundedLinkedQueue,BoundedBuffer, BoundedPriorityQueue, SynchronousChannel, Slot<br /><br />通道例子<br />class Service { // ...<br />final Channel msgQ = new LinkedQueue();<br />public void serve() throws InterruptedException {<br />String status = doService();<br />msgQ.put(status);<br />}<br />public Service() { // start background thread<br />Runnable logger = new Runnable() {<br />public void run() {<br />try {<br />for(;;)<br />System.out.println(msqQ.take());<br />}<br />catch(InterruptedException ie) {} }<br />};<br />new Thread(logger).start();<br />}<br />}<br />在后台服务器中，缓冲和队列都是最常用到的。试想，如果对所有远端的请求不排个队列，让它们一拥而上的去争夺cpu、内存、资源，那服务器瞬间不当掉才怪。而在这里，成熟的队列和缓冲实现已经提供，您只需要对其进行正确初始化并使用即可，大大缩短了开发时间。<br /><br />4.2.3执行器(Executor)<br />Executor是这里最重要、也是我们往往最终写程序要用到的，下面重点对其进行介绍。<br />类似线程的类的主接口<br />- 线程池<br />- 轻量级运行框架<br />- 可以定制调度算法<br /><br />只需要支持execute(Runnable r)<br />- 同Thread.start类似<br /><br />实现<br />- PooledExecutor, ThreadedExecutor, QueuedExecutor, FJTaskRunnerGroup<br /><br />PooledExecutor（线程池执行器）是个最常用到的类，以它为例：<br />可修改得属性如下：<br />- 任务队列的类型<br />- 最大线程数<br />- 最小线程数<br />- 预热(预分配)和立即(分配)线程<br />- 保持活跃直到工作线程结束<br />-- 以后如果需要可能被一个新的代替<br />- 饱和(Saturation)协议<br />-- 阻塞，丢弃，生产者运行，等等<br /><br />可不要小看上面这数条属性，对这些属性的设置完全可以等同于您自己撰写的线程池的成百上千行代码。下面以笔者撰写过得一个GIS服务器为例：<br />该GIS服务器是一个典型的“请求－服务”类型的服务器，遵循后端程序设计的一般框架。首先对所有的请求按照先来先服务排入一个请求队列，如果瞬间到达的 请求超过了请求队列的容量，则将溢出的请求转移至一个临时队列。如果临时队列也排满了，则对以后达到的请求给予一个“服务器忙”的提示后将其简单抛弃。这 个就够忙活一阵的了。<br />然后，结合链表结构实现一个线程池，给池一个初始容量。如果该池满，以x2的策略将池的容量动态增加一倍，依此类推，直到总线程数服务达到系统能力上限， 之后线程池容量不在增加，所有请求将等待一个空余的返回线程。每从池中得到一个线程，该线程就开始最请求进行GIS信息的服务，如取坐标、取地图，等等。 服务完成后，该线程返回线程池继续为请求队列离地后续请求服务，周而复始。当时用矢量链表来暂存请求，用wait()、 notify() 和 synchronized等原语结合矢量链表实现线程池，总共约600行程序，而且在运行时间较长的情况下服务器不稳定，线程池被取用的线程有异常消失的 情况发生。而使用util.concurrent相关类之后，仅用了几十行程序就完成了相同的工作而且服务器运行稳定，线程池没有丢失线程的情况发生。由 此可见util.concurrent包极大的提高了开发效率，为项目节省了大量的时间。<br />使用PooledExecutor例子<br />import java.net.*;<br />/**<br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Title: </font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Description: 负责初始化线程池以及启动服务器</font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Copyright: Copyright (c) 2003</font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Company: </font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* @author not attributable<br />* @version 1.0<br />*/<br />public class MainServer {<br />//初始化常量<br />public static final int MAX_CLIENT=100; //系统最大同时服务客户数<br />//初始化线程池<br />public static final PooledExecutor pool =<br />new PooledExecutor(new BoundedBuffer(10), MAX_CLIENT); //chanel容量为10,<br />//在这里为线程池初始化了一个<br />//长度为10的任务缓冲队列。<br /><br />public MainServer() {<br />//设置线程池运行参数<br />pool.setMinimumPoolSize(5); //设置线程池初始容量为5个线程<br />pool.discardOldestWhenBlocked();//对于超出队列的请求，使用了抛弃策略。<br />pool.createThreads(2); //在线程池启动的时候，初始化了具有一定生命周期的2个“热”线程<br />}<br /><br />public static void main(String[] args) {<br />MainServer MainServer1 = new MainServer();<br />new HTTPListener().start();//启动服务器监听和处理线程<br />new manageServer().start();//启动管理线程<br />}<br />}<br /><br /><br /><br />类HTTPListener<br />import java.net.*;<br />/**<br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Title: </font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Description: 负责监听端口以及将任务交给线程池处理</font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Copyright: Copyright (c) 2003<br /></font></font><font title="2003-11-30 11:56:28"><font color="blue">* </font></font><font title="2003-11-30 11:56:28"><font color="blue">Company: </font></font><font title="2003-11-30 11:56:28"><font color="blue"><br />* @author not attributable<br />* @version 1.0<br />*/<br />public class HTTPListener extends Thread{<br />public HTTPListener() {<br />}<br />public void run(){<br />try{<br />ServerSocket server=null;<br />Socket clientconnection=null;<br />server = new ServerSocket(8008);//服务套接字监听某地址端口对<br />while(true){//无限循环<br />clientconnection =server.accept();<br />System.out.println("Client connected in!");<br />//使用线程池启动服务<br />MainServer.pool.execute(new HTTPRequest(clientconnection));//如果收到一个请求，则从线程池中取一个线程进行服务，任务完成后，该线程自动返还线程池<br />}<br />}catch(Exception e){<br />System.err.println("Unable to start serve listen:"+e.getMessage());<br />e.printStackTrace();<br />}<br />}<br />}<br /><br />关于util.concurrent工具包就有选择的介绍到这，更详细的信息可以阅读这些java源代码的API文档。Doug Lea是个很具有“open”精神的作者，他将util.concurrent工具包的java源代码全部公布出来，有兴趣的读者可以下载这些源代码并细 细品味。 <br /><br />5　　　　结束语<br />以上内容介绍了线程池基本原理以及设计后台服务程序应考虑到的问题，并结合实例详细介绍了重要的多线程开发工具包util.concurrent的构架和使用。结合使用已有完善的开发包，后端服务程序的开发周期将大大缩短，同时程序性能也有了保障。<br /><br /><br /><br />参考文献<br />[1] Chad Darby,etc. 《Beginning Java Networking》. 电子工业出版社. 2002年3月. <br />[2] util.concurrent 说明文件 http://gee.cs.oswego.edu/dl/cpjslides/util.pdf <br />[3] 幸勇.线程池的介绍及简单实现.http://www-900.ibm.com/developerWorks/cn/java/l-threadPool/index.shtml<br />2002年8月<br />[4]BrianGoetz. 我的线程到哪里去了http://www-900.cn.ibm.com/developerworks/cn/java/j-jtp0924/index.shtml<br /></font></font></p>
<img src ="http://www.blogjava.net/jesson2005/aggbug/96927.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-01-31 13:52 <a href="http://www.blogjava.net/jesson2005/articles/96927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程 — Concurrent包简介</title><link>http://www.blogjava.net/jesson2005/articles/96917.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Wed, 31 Jan 2007 05:08:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/96917.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/96917.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/96917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/96917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/96917.html</trackback:ping><description><![CDATA[
		<h2 id="post-15">
				<a title="Permanent Link to 多线程 — Concurrent包简介" href="http://www.seofchina.com/blogs/yy/?p=15" rel="bookmark">多线程 — Concurrent包简介</a>
		</h2>
		<p>
				<strong>Util.concurrent工具包概述</strong>
				<br />
				<strong>Doug Lea</strong>
				<br />State University of New York at Oswego<br />dl@cs.oswego.edu</p>
		<p>http://gee.cs.oswego.edu</p>
		<p>翻译：</p>
		<p>Cocia Lin(cocia@163.com)</p>
		<p>Huihoo.org</p>
		<p>原文</p>
		<p>http://gee.cs.oswego.edu/dl/cpjslides/util.pdf</p>
		<p>要点<br />–目标和结构</p>
		<p>–主要的接口和实现</p>
		<p>Sync:获得/释放(acquire/release) 协议</p>
		<p>Channel:放置/取走(put/take) 协议</p>
		<p>Executor:执行Runnable任务</p>
		<p>–每一个部分都有一些关联的接口和支持类</p>
		<p>–简单的涉及其他的类和特性</p>
		<p>目标<br />–一些简单的接口</p>
		<p>-但是覆盖大部分程序员需要小心处理代码的问题</p>
		<p>– 高质量实现</p>
		<p>-正确的，保守的，有效率的，可移植的</p>
		<p>–可能作为将来标准的基础</p>
		<p>-获取经验和收集反馈信息</p>
		<p>Sync<br />– acquire/release协议的主要接口</p>
		<p>-用来定制锁，资源管理，其他的同步用途</p>
		<p>- 高层抽象接口</p>
		<p>- 没有区分不同的加锁用法</p>
		<p>–实现</p>
		<p>-Mutex, ReentrantLock, Latch, CountDown,Semaphore, WaiterPreferenceSemaphore,FIFOSemaphore, PrioritySemaphore</p>
		<p>n 还有，有几个简单的实现，例如ObservableSync, LayeredSync</p>
		<p>独占锁<br /><code>try {</code></p>
		<p>lock.acquire();</p>
		<p>try {</p>
		<p>action();</p>
		<p>}</p>
		<p>finally {</p>
		<p>lock.release();</p>
		<p>}</p>
		<p>}</p>
		<p>catch (InterruptedException ie) { ... }</p>
		<p>– Java同步块不适用的时候使用它</p>
		<p>- 超时，回退(back-off)</p>
		<p>- 确保可中断</p>
		<p>- 大量迅速锁定</p>
		<p>- 创建Posix风格应用(condvar)</p>
		<p>
				<a id="more-15">
				</a>
		</p>
		<p>独占例子<br /><code>class ParticleUsingMutex {</code></p>
		<p>int x; int y;</p>
		<p>final Random rng = new Random();</p>
		<p>final Mutex mutex = new Mutex();</p>
		<p>public void move() {</p>
		<p>try {</p>
		<p>mutex.acquire();</p>
		<p>try { x += rng.nextInt(2)-1; y += rng.nextInt(2)-1; }</p>
		<p>finally { mutex.release(); }</p>
		<p>}</p>
		<p>catch (InterruptedException ie) {</p>
		<p>Thread.currentThread().interrupt(); }</p>
		<p>}</p>
		<p>public void draw(Graphics g) {</p>
		<p>int lx, ly;</p>
		<p>try {</p>
		<p>mutex.acquire();</p>
		<p>try { lx = x; ly = y; }</p>
		<p>finally { mutex.release(); }</p>
		<p>}</p>
		<p>catch (InterruptedException ie) {</p>
		<p>Thread.currentThread().interrupt(); return; }</p>
		<p>g.drawRect(lx, ly, 10, 10);</p>
		<p>}</p>
		<p>}</p>
		<p>回退(Backoff)例子<br /><code>class CellUsingBackoff {</code></p>
		<p>private long val;</p>
		<p>private final Mutex mutex = new Mutex();</p>
		<p>void swapVal(CellUsingBackoff other)</p>
		<p>throws InterruptedException {</p>
		<p>if (this == other) return; // alias check</p>
		<p>for (;;) {</p>
		<p>mutex.acquire();</p>
		<p>try {</p>
		<p>I f (other.mutex.attempt(0)) {</p>
		<p>try {</p>
		<p>long t = val;</p>
		<p>val = other.val;</p>
		<p>other.val = t;</p>
		<p>return;</p>
		<p>}</p>
		<p>finally { other.mutex.release(); }</p>
		<p>}</p>
		<p>}</p>
		<p>finally { mutex.release(); };</p>
		<p>Thread.sleep(100); // heuristic retry interval</p>
		<p>}</p>
		<p>}</p>
		<p>}</p>
		<p>读写锁<br />interface ReadWriteLock {</p>
		<p>Sync readLock();</p>
		<p>Sync writeLock();</p>
		<p>}</p>
		<p>– 管理一对锁</p>
		<p>- 和普通的锁一样的使用习惯</p>
		<p>– 对集合类很有用</p>
		<p>-半自动的方式实现SyncSet, SyncMap, …</p>
		<p>– 实现者使用不同的锁策略</p>
		<p>- WriterPreference, ReentrantWriterPreference,</p>
		<p>ReaderPreference, FIFO</p>
		<p>ReadWriteLock例子<br />– 示范在读写锁中执行任何Runnable的包装类</p>
		<p>class WithRWLock {</p>
		<p>final ReadWriteLock rw;</p>
		<p>public WithRWLock(ReadWriteLock l) { rw = l; }</p>
		<p>public void performRead(Runnable readCommand)</p>
		<p>throws InterruptedException {</p>
		<p>rw.readLock().acquire();</p>
		<p>try { readCommand.run(); }</p>
		<p>finally { rw.readlock().release(); }</p>
		<p>}</p>
		<p>public void performWrite(…) // similar</p>
		<p>}</p>
		<p>闭锁(Latch)<br />– 闭锁是开始时设置为false,但一旦被设置为true，他将永远保持true状态</p>
		<p>- 初始化标志</p>
		<p>- 流结束定位</p>
		<p>- 线程中断</p>
		<p>- 事件出发指示器</p>
		<p>– CountDown和他有点类似，不同的是，CountDown需要一定数量的触发设置，而不是一次</p>
		<p>– 非常简单，但是广泛使用的类</p>
		<p>- 替换容易犯错的开发代码</p>
		<p>Latch Example 闭锁例子<br />class Worker implements Runnable {</p>
		<p>Latch startSignal;</p>
		<p>Worker(Latch l) { startSignal = l; }</p>
		<p>public void run() {</p>
		<p>startSignal.acquire();</p>
		<p>// … doWork();</p>
		<p>}</p>
		<p>}</p>
		<p>class Driver { // …</p>
		<p>void main() {</p>
		<p>Latch ss = new Latch();</p>
		<p>for (int i = 0; i &lt; N; ++i) // make threads</p>
		<p>new Thread(new Worker(ss)).start();</p>
		<p>doSomethingElse(); // don’t let run yet</p>
		<p>ss.release(); // now let all threads proceed</p>
		<p>}</p>
		<p>}</p>
		<p>信号(Semaphores)<br />-- 服务于数量有限的占有者</p>
		<p>- 使用许可数量构造对象(通常是0)</p>
		<p>- 如果需要一个许可才能获取，等待，然后取走一个许可</p>
		<p>- 释放的时候将许可添加回来</p>
		<p>-- 但是真正的许可并没有转移(But no actual permits change hands.)</p>
		<p>- 信号量仅仅保留当前的计数值</p>
		<p>-- 应用程序</p>
		<p>- 锁：一个信号量可以被用作互斥体(mutex)</p>
		<p>- 一个独立的等待缓存或者资源控制的操作</p>
		<p>- 设计系统是想忽略底层的系统信号</p>
		<p>-- (phores ‘remember’ past signals)记住已经消失的信号量</p>
		<p>
				<!--more-->
		</p>
		<p>信号量例子<br />class Pool {</p>
		<p>ArrayList items = new ArrayList();</p>
		<p>HashSet busy = new HashSet();</p>
		<p>final Semaphore available;</p>
		<p>public Pool(int n) {</p>
		<p>available = new Semaphore(n);</p>
		<p>// … somehow initialize n items …;</p>
		<p>}</p>
		<p>public Object getItem() throws InterruptedException {</p>
		<p>available.acquire();</p>
		<p>return doGet();</p>
		<p>}</p>
		<p>public void returnItem(Object x) {</p>
		<p>if (doReturn(x)) available.release();</p>
		<p>}</p>
		<p>synchronized Object doGet() {</p>
		<p>Object x = items.remove(items.size()-1);</p>
		<p>busy.add(x); // put in set to check returns</p>
		<p>return x;</p>
		<p>}</p>
		<p>synchronized boolean doReturn(Object x) {</p>
		<p>return busy.remove(x); // true if was present</p>
		<p>}</p>
		<p>}</p>
		<p>屏障(Barrier)<br />– 多部分同步接口</p>
		<p>- 每一部分都必须等待其他的分不撞倒屏障</p>
		<p>– CyclicBarrier类</p>
		<p>- CountDown的一个可以重新设置的版本</p>
		<p>- 对于反复划分算法很有用(iterative partitioning algorithms)</p>
		<p>– Rendezvous类</p>
		<p>- 一个每部分都能够和其他部分交换信息的屏障</p>
		<p>- 行为类似同时的在一个同步通道上put和take</p>
		<p>- 对于资源交换协议很有用(resource-exchange protocols)</p>
		<p>通道(Channel)<br />–为缓冲，队列等服务的主接口</p>
		<p>– 具体实现</p>
		<p>- LinkedQueue, BoundedLinkedQueue,BoundedBuffer, BoundedPriorityQueue,SynchronousChannel, Slot</p>
		<p>通道属性<br />– 被定义为Puttable和Takable的子接口</p>
		<p>- 允许安装生产者/消费者模式执行</p>
		<p>– 支持可超时的操作offer和poll</p>
		<p>- 当超时值是0时，可能会被阻塞</p>
		<p>- 所有的方法能够抛出InterruptedException异常</p>
		<p>– 没有接口需要size方法</p>
		<p>- 但是一些实现定义了这个方法</p>
		<p>- BoundedChannel有capacity方法</p>
		<p>通道例子<br />class Service { // …</p>
		<p>final Channel msgQ = new LinkedQueue();</p>
		<p>public void serve() throws InterruptedException {</p>
		<p>String status = doService();</p>
		<p>msgQ.put(status);</p>
		<p>}</p>
		<p>public Service() { // start background thread</p>
		<p>Runnable logger = new Runnable() {</p>
		<p>public void run() {</p>
		<p>try {</p>
		<p>for(;;)</p>
		<p>System.out.println(msqQ.take());</p>
		<p>}</p>
		<p>catch(InterruptedException ie) {} }</p>
		<p>};</p>
		<p>new Thread(logger).start();</p>
		<p>}</p>
		<p>}</p>
		<p>运行器(Executor)<br />– 类似线程的类的主接口</p>
		<p>- 线程池</p>
		<p>- 轻量级运行框架</p>
		<p>- 可以定制调度算法</p>
		<p>– 只需要支持execute(Runnable r)</p>
		<p>- 同Thread.start类似</p>
		<p>– 实现</p>
		<p>- PooledExecutor, ThreadedExecutor,QueuedExecutor, FJTaskRunnerGroup</p>
		<p>- 相关的ThreadFactory类允许大多数的运行器通过定制属性使用线程</p>
		<p>PooledExecutor<br />– 一个可调的工作者线程池，可修改得属性如下：</p>
		<p>- 任务队列的类型</p>
		<p>- 最大线程数</p>
		<p>- 最小线程数</p>
		<p>- 预热(预分配)和立即(分配)线程</p>
		<p>- 保持活跃直到工作线程结束</p>
		<p>– 以后如果需要可能被一个新的代替</p>
		<p>- 饱和(Saturation)协议</p>
		<p>– 阻塞，丢弃，生产者运行，等等</p>
		<p>PooledExecutor例子<br />class WebService {</p>
		<p>public static void main(String[] args) {</p>
		<p>PooledExecutor pool =</p>
		<p>new PooledExecutor(new BoundedBuffer(10), 20);</p>
		<p>pool.createThreads(4);</p>
		<p>try {</p>
		<p>ServerSocket socket = new ServerSocket(9999);</p>
		<p>for (;;) {</p>
		<p>final Socket connection = socket.accept();</p>
		<p>pool.execute(new Runnable() {</p>
		<p>public void run() {</p>
		<p>new Handler().process(connection);</p>
		<p>}});</p>
		<p>}</p>
		<p>}</p>
		<p>catch(Exception e) { } // die</p>
		<p>}</p>
		<p>}</p>
		<p>class Handler { void process(Socket s); }</p>
		<p>前景(Future)和可调用(Callable)<br />– Callabe是类似于Runnable的接口，用来作为参数和传递结果</p>
		<p>interface Callable {</p>
		<p>Object call(Object arg) throws Exception;</p>
		<p>}</p>
		<p>– FutureResult管理Callable的异步执行</p>
		<p>class FutureResult { // …</p>
		<p>// block caller until result is ready</p>
		<p>public Object get()</p>
		<p>throws InterruptedException, InvocationTargetException;</p>
		<p>public void set(Object result); // unblocks get</p>
		<p>// create Runnable that can be used with an Executor</p>
		<p>public Runnable setter(Callable function);</p>
		<p>}</p>
		<p>FutureResult例子<br />class ImageRenderer { Image render(byte[] raw); }</p>
		<p>class App { // …</p>
		<p>Executor executor = …; // any executor</p>
		<p>ImageRenderer renderer = new ImageRenderer();</p>
		<p>public void display(byte[] rawimage) {</p>
		<p>try {</p>
		<p>FutureResult futureImage = new FutureResult();</p>
		<p>Runnable cmd = futureImage.setter(new Callable(){</p>
		<p>public Object call() {</p>
		<p>return renderer.render(rawImage);</p>
		<p>}});</p>
		<p>executor.execute(cmd);</p>
		<p>drawBorders(); // do other things while executing</p>
		<p>drawCaption();</p>
		<p>drawImage((Image)(futureImage.get())); // use future</p>
		<p>}</p>
		<p>catch (Exception ex) {</p>
		<p>cleanup();</p>
		<p>return;</p>
		<p>}</p>
		<p>}</p>
		<p>}</p>
		<p>其他的类<br />– CopyOnWriteArrayList</p>
		<p>- 支持整个集合复制时每一个修改的无锁访问</p>
		<p>- 适合大多数的多路广播应用程序</p>
		<p>– 工具包还包括了一个java.beans多路广播类的COW版本</p>
		<p>– SynchronizedDouble, SynchronizedInt,SynchronizedRef, etc</p>
		<p>- 类似于java.lang.Double，提供可变操作的同步版本.例如，addTo,inc</p>
		<p>- 添加了一些象swap,commit这样的实用操作</p>
		<p>未来计划<br />– 并发数据构架</p>
		<p>- 一组繁重线程连接环境下有用的工具集合</p>
		<p>–支持侧重I/O的程序</p>
		<p>- 事件机制的IO系统</p>
		<p>– 小版本的实现</p>
		<p>- 例如SingleSourceQueue</p>
		<p>–小幅度的改善</p>
		<p>- 使运行器更容易使用</p>
		<p>– 替换</p>
		<p>- JDK1.3 java.util.Timer 被ClockDaemon取代</p>
<img src ="http://www.blogjava.net/jesson2005/aggbug/96917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-01-31 13:08 <a href="http://www.blogjava.net/jesson2005/articles/96917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 线程/内存模型的缺陷和增强---摘录《J2SE进阶》</title><link>http://www.blogjava.net/jesson2005/articles/96868.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Wed, 31 Jan 2007 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/96868.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/96868.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/96868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/96868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/96868.html</trackback:ping><description><![CDATA[
		<h1 class="main_left_block_title" align="center">         Java 线程/内存模型的缺陷和增强</h1>
		<!-- 内容区块 -->
		<div class="container" id="articleinfo">
				<!--GG Begin:-->
				<div class="articleinfo">
						<br />
						<br />        本文是由JR主持写作的《J2SE进阶》一书的部分章节整理而成，《J2SE进阶》正在写作、完善阶段。您阅读后，有任何建议、批评，请 和我联系 ，或在 这儿留言 。《J2SE进阶》写作项目组感谢您阅读本文。 <br /><br />        Java在语言层次上实现了对线程的支持。它提供了Thread/Runnable/ThreadGroup等一系列封装的类和接口，让程序员可以高效的开发Java多线程应用。为了实现同步，Java提供了synchronize关键字以及object的wait()/notify()机制，可是在简单易用的背后，应藏着更为复杂的玄机，很多问题就是由此而起。 </div>
				<div class="articlecontent">
						<h3>
								<strong>一、Java内存模型 </strong>
						</h3>
						<p style="TEXT-INDENT: 2em">在了解Java的同步秘密之前，先来看看JMM(Java Memory Model)。 <br />Java被设计为跨平台的语言，在内存管理上，显然也要有一个统一的模型。而且Java语言最大的特点就是废除了指针，把程序员从痛苦中解脱出来，不用再考虑内存使用和管理方面的问题。 <br />可惜世事总不尽如人意，虽然JMM设计上方便了程序员，但是它增加了虚拟机的复杂程度，而且还导致某些编程技巧在Java语言中失效。 <br /><br />JMM主要是为了规定了线程和内存之间的一些关系。对Java程序员来说只需负责用synchronized同步关键字，其它诸如与线程/内存之间进行数据交换/同步等繁琐工作均由虚拟机负责完成。如图1所示：根据JMM的设计，系统存在一个主内存(Main Memory)，Java中所有变量都储存在主存中，对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory)，工作内存中保存的是主存中某些变量的拷贝，线程对所有变量的操作都是在工作内存中进行，线程之间无法相互直接访问，变量传递均需要通过主存完成。 
</p>
						<p align="center">
								<img src="http://www.7880.com/Upload/2006-06/F53D46F47E69FC5E4DB0B9F69366DAB7.gif" />
						</p>
						<p align="left">图1 Java内存模型示例图 <br /><br />线程若要对某变量进行操作，必须经过一系列步骤：首先从主存复制/刷新数据到工作内存，然后执行代码，进行引用/赋值操作，最后把变量内容写回Main Memory。Java语言规范(JLS)中对线程和主存互操作定义了6个行为，分别为load，save，read，write，assign和use，这些操作行为具有原子性，且相互依赖，有明确的调用先后顺序。具体的描述请参见JLS第17章。 <br /><br />我们在前面的章节介绍了synchronized的作用，现在，从JMM的角度来重新审视synchronized关键字。 <br />假设某条线程执行一个synchronized代码段，其间对某变量进行操作，JVM会依次执行如下动作： <br />(1) 获取同步对象monitor (lock) <br />(2) 从主存复制变量到当前工作内存 (read and load) <br />(3) 执行代码，改变共享变量值 (use and assign) <br />(4) 用工作内存数据刷新主存相关内容 (store and write) <br />(5) 释放同步对象锁 (unlock) <br />可见，synchronized的另外一个作用是保证主存内容和线程的工作内存中的数据的一致性。如果没有使用synchronized关键字，JVM不保证第2步和第4步会严格按照上述次序立即执行。因为根据JLS中的规定，线程的工作内存和主存之间的数据交换是松耦合的，什么时候需要刷新工作内存或者更新主内存内容，可以由具体的虚拟机实现自行决定。如果多个线程同时执行一段未经synchronized保护的代码段，很有可能某条线程已经改动了变量的值，但是其他线程却无法看到这个改动，依然在旧的变量值上进行运算，最终导致不可预料的运算结果。 </p>
						<h3>
								<strong>二、DCL失效 </strong>
						</h3>
						<p style="TEXT-INDENT: 2em">这一节我们要讨论的是一个让Java丢脸的话题：DCL失效。在开始讨论之前，先介绍一下LazyLoad，这种技巧很常用，就是指一个类包含某个成员变量，在类初始化的时候并不立即为该变量初始化一个实例，而是等到真正要使用到该变量的时候才初始化之。 <br />例如下面的代码： <br />代码1 </p>
						<p class="code">
								<strong>class </strong>Foo { <br /><strong>private </strong>Resource res = <strong>null </strong>; <br /><br /><strong>public </strong>Resource getResource() { <br /><strong>if </strong>(res == <strong>null </strong>) <br />res = <strong>new </strong>Resource(); <br /><strong>return </strong>res; <br />} <br />} </p>
						<p style="TEXT-INDENT: 2em">由于LazyLoad可以有效的减少系统资源消耗，提高程序整体的性能，所以被广泛的使用，连Java的缺省类加载器也采用这种方法来加载Java类。 <br />在单线程环境下，一切都相安无事，但如果把上面的代码放到多线程环境下运行，那么就可能会出现问题。假设有2条线程，同时执行到了if(res == null)，那么很有可能res被初始化2次，为了避免这样的Race Condition，得用synchronized关键字把上面的方法同步起来。代码如下： <br />代码2 </p>
						<p class="code">
								<strong>Class </strong>Foo { <br />Private Resource res = <strong>null </strong>; <br />Public <strong>synchronized </strong>Resource getResource() { <br />If (res == <strong>null </strong>) <br />res = <strong>new </strong>Resource(); <br /><strong>return </strong>res; <br />} <br />} </p>
						<p style="TEXT-INDENT: 2em">现在Race Condition解决了，一切都很好。 <br /><br />N天过后，好学的你偶然看了一本Refactoring的魔书，深深为之打动，准备自己尝试这重构一些以前写过的程序，于是找到了上面这段代码。你已经不再是以前的Java菜鸟，深知synchronized过的方法在速度上要比未同步的方法慢上100倍，同时你也发现，只有第一次调用该方法的时候才需要同步，而一旦res初始化完成，同步完全没必要。所以你很快就把代码重构成了下面的样子： <br />代码3 </p>
						<p class="code">
								<strong>Class </strong>Foo { <br />Private Resource res = <strong>null </strong>; <br />Public Resource getResource() { <br />If (res == <strong>null </strong>){ <br /><strong>synchronized </strong>( <strong>this </strong>){ <br /><strong>if </strong>(res == <strong>null </strong>){ <br />res = <strong>new </strong>Resource(); <br />} <br />} <br />} <br /><strong>return </strong>res; <br />} <br />} </p>
						<p style="TEXT-INDENT: 2em">这种看起来很完美的优化技巧就是Double-Checked Locking。但是很遗憾，根据Java的语言规范，上面的代码是不可靠的。 <br /><br />造成DCL失效的原因之一是编译器的优化会调整代码的次序。只要是在单个线程情况下执行结果是正确的，就可以认为编译器这样的“自作主张的调整代码次序”的行为是合法的。JLS在某些方面的规定比较自由，就是为了让JVM有更多余地进行代码优化以提高执行效率。而现在的CPU大多使用超流水线技术来加快代码执行速度，针对这样的CPU，编译器采取的代码优化的方法之一就是在调整某些代码的次序，尽可能保证在程序执行的时候不要让CPU的指令流水线断流，从而提高程序的执行速度。正是这样的代码调整会导致DCL的失效。为了进一步证明这个问题，引用一下《DCL Broken Declaration》文章中的例子： <br />设一行Java代码： </p>
						<p class="code">Objects[i].reference = <strong>new </strong><strong>Object </strong>(); </p>
						<p style="TEXT-INDENT: 2em">经过Symantec JIT编译器编译过以后，最终会变成如下汇编码在机器中执行： </p>
						<br />0206106A mov eax,0F97E78h <br />0206106F call 01F6B210 ;为Object申请内存空间 <br />; 返回值放在eax中 <br />02061074 mov dword ptr [ebp],eax ; EBP 中是objects[i].reference的地址 <br />; 将返回的空间地址放入其中 <br />; 此时Object尚未初始化 <br />02061077 mov ecx,dword ptr [eax] ; dereference eax所指向的内容 <br />; 获得新创建对象的起始地址 <br />02061079 mov dword ptr [ecx],100h ; 下面4行是内联的构造函数 <br />0206107F mov dword ptr [ecx+4],200h <br />02061086 mov dword ptr [ecx+8],400h <br />0206108D mov dword ptr [ecx+0Ch],0F84030h <br /><p style="TEXT-INDENT: 2em">可见，Object构造函数尚未调用，但是已经能够通过objects[i].reference获得Object对象实例的引用。 <br />如果把代码放到多线程环境下运行，某线程在执行到该行代码的时候JVM或者操作系统进行了一次线程切换，其他线程显然会发现msg对象已经不为空，导致Lazy load的判断语句if(objects[i].reference == null)不成立。线程认为对象已经建立成功，随之可能会使用对象的成员变量或者调用该对象实例的方法，最终导致不可预测的错误。 <br /><br />原因之二是在共享内存的SMP机上，每个CPU有自己的Cache和寄存器，共享同一个系统内存。所以CPU可能会动态调整指令的执行次序，以更好的进行并行运算并且把运算结果与主内存同步。这样的代码次序调整也可能导致DCL失效。回想一下前面对Java内存模型的介绍，我们这里可以把Main Memory看作系统的物理内存，把Thread Working Memory认为是CPU内部的Cache和寄存器，没有synchronized的保护，Cache和寄存器的内容就不会及时和主内存的内容同步，从而导致一条线程无法看到另一条线程对一些变量的改动。 <br />结合代码3来举例说明，假设Resource类的实现如下： </p><p class="code"><strong>Class </strong>Resource{ <br /><strong>Object </strong>obj; <br />} </p><p style="TEXT-INDENT: 2em">即Resource类有一个obj成员变量引用了Object的一个实例。假设2条线程在运行，其状态用如下简化图表示： 
</p><p align="center"><img src="http://www.7880.com/Upload/2006-06/BAD3F09601E2FE28FD1A3FAF243D7AB8.gif" /></p><p align="left">图2 <br />现在Thread-1构造了Resource实例，初始化过程中改动了obj的一些内容。退出同步代码段后，因为采取了同步机制，Thread-1所做的改动都会反映到主存中。接下来Thread-2获得了新的Resource实例变量res，由于没有使用synchronized保护所以Thread-2不会进行刷新工作内存的操作。假如之前Thread-2的工作内存中已经有了obj实例的一份拷贝，那么Thread-2在对obj执行use操作的时候就不会去执行load操作,这样一来就无法看到Thread-1对obj的改变，这显然会导致错误的运算结果。此外，Thread-1在退出同步代码段的时刻对ref和obj执行的写入主存的操作次序也是不确定的，所以即使Thread-2对obj执行了load操作，也有可能只读到obj的初试状态的数据。(注：这里的load/use均指JMM定义的操作) <br /><br />有很多人不死心，试图想出了很多精妙的办法来解决这个问题，但最终都失败了。事实上，无论是目前的JMM还是已经作为JSR提交的JMM模型的增强，DCL都不能正常使用。在William Pugh的论文《Fixing the Java Memory Model》中详细的探讨了JMM的一些硬伤，更尝试给出一个新的内存模型，有兴趣深入研究的读者可以参见文后的参考资料。 <br /><br />如果你设计的对象在程序中只有一个实例，即singleton的，有一种可行的解决办法来实现其LazyLoad：就是利用类加载器的LazyLoad特性。代码如下： </p><p class="code"><strong>Class </strong>ResSingleton { <br /><strong>public </strong><strong>static </strong>Resource res = <strong>new </strong>Resource(); <br />} </p><p style="TEXT-INDENT: 2em">这里ResSingleton只有一个静态成员变量。当第一次使用ResSingleton.res的时候，JVM才会初始化一个Resource实例，并且JVM会保证初始化的结果及时写入主存，能让其他线程看到，这样就成功的实现了LazyLoad。 <br />除了这个办法以外，还可以使用ThreadLocal来实现DCL的方法，但是由于ThreadLocal的实现效率比较低，所以这种解决办法会有较大的性能损失，有兴趣的读者可以参考文后的参考资料。 <br /><br />最后要说明的是，对于DCL是否有效，个人认为更多的是一种带有学究气的推断和讨论。而从纯理论的角度来看，存取任何可能共享的变量（对象引用）都需要同步保护，否则都有可能出错，但是处处用synchronized又会增加死锁的发生几率，苦命的程序员怎么来解决这个矛盾呢？事实上，在很多Java开源项目（比如Ofbiz/Jive等）的代码中都能找到使用DCL的证据，我在具体的实践中也没有碰到过因DCL而发生的程序异常。个人的偏好是：不妨先大胆使用DCL，等出现问题再用synchronized逐步排除之。也许有人偏于保守，认为稳定压倒一切，那就不妨先用synchronized同步起来，我想这是一个见仁见智的问题，而且得针对具体的项目具体分析后才能决定。还有一个办法就是写一个测试案例来测试一下系统是否存在DCL现象，附带的光盘中提供了这样一个例子，感兴趣的读者可以自行编译测试。不管结果怎样，这样的讨论有助于我们更好的认识JMM，养成用多线程的思路去分析问题的习惯，提高我们的程序设计能力。 </p><h3><strong>三、Java线程同步增强包 </strong></h3><p style="TEXT-INDENT: 2em">相信你已经了解了Java用于同步的3板斧：synchronized/wait/notify，它们的确简单而有效。但是在某些情况下，我们需要更加复杂的同步工具。有些简单的同步工具类，诸如ThreadBarrier，Semaphore，ReadWriteLock等，可以自己编程实现。现在要介绍的是牛人Doug Lea的Concurrent包。这个包专门为实现Java高级并行程序所开发，可以满足我们绝大部分的要求。更令人兴奋的是，这个包公开源代码，可自由下载。且在JDK1.5中该包将作为SDK一部分提供给Java开发人员。 <br /><br />Concurrent Package提供了一系列基本的操作接口，包括sync，channel，executor,barrier,callable等。这里将对前三种接口及其部分派生类进行简单的介绍。 <br /><br /><strong>sync接口： </strong>专门负责同步操作，用于替代Java提供的synchronized关键字，以实现更加灵活的代码同步。其类关系图如下： 
</p><p align="center"><img src="http://www.7880.com/Upload/2006-06/456412BE703E36D05B0B06AE0B58B470.jpg" /></p>图3 Concurrent包Sync接口类关系图 <br />Semaphore：和前面介绍的代码类似，可用于pool类实现资源管理限制。提供了acquire()方法允许在设定时间内尝试锁定信号量，若超时则返回false。 <br /><br />Mutex：和Java的synchronized类似，与之不同的是，synchronized的同步段只能限制在一个方法内，而Mutex对象可以作为参数在方法间传递，所以可以把同步代码范围扩大到跨方法甚至跨对象。 <br /><br />NullSync：一个比较奇怪的东西，其方法的内部实现都是空的，可能是作者认为如果你在实际中发现某段代码根本可以不用同步，但是又不想过多改动这段代码，那么就可以用NullSync来替代原来的Sync实例。此外，由于NullSync的方法都是synchronized，所以还是保留了“内存壁垒”的特性。 <br /><br />ObservableSync：把sync和observer模式结合起来，当sync的方法被调用时，把消息通知给订阅者，可用于同步性能调试。 <br /><br />TimeoutSync：可以认为是一个adaptor，其构造函数如下： <br />public TimeoutSync(Sync sync, long timeout){…} <br />具体上锁的代码靠构造函数传入的sync实例来完成，其自身只负责监测上锁操作是否超时，可与SyncSet合用。 <br /><br /><strong>Channel接口： </strong>代表一种具备同步控制能力的容器，你可以从中存放/读取对象。不同于JDK中的Collection接口，可以把Channel看作是连接对象构造者(Producer)和对象使用者(Consumer)之间的一根管道。如图所示： 
<p align="center"><img src="http://www.7880.com/Upload/2006-06/12E19C54D0295DD4DD67E6E3EE24A833.jpg" /></p>图4 Concurrent包Channel接口示意图 <br /><br />通过和Sync接口配合，Channel提供了阻塞式的对象存取方法（put/take）以及可设置阻塞等待时间的offer/poll方法。实现Channel接口的类有LinkedQueue，BoundedLinkedQueue，BoundedBuffer，BoundedPriorityQueue，SynchronousChannel，Slot等。 
<p align="center"><img src="http://www.7880.com/Upload/2006-06/C9F4C25C3349969D23F503B120F1D6E8.jpg" /></p>图5 Concurrent包Channel接口部分类关系图 <br /><br />使用Channel我们可以很容易的编写具备消息队列功能的代码，示例如下： <br />代码4 
<p></p><p class="code"><strong>Package </strong>org.javaresearch.j2seimproved.thread; <br /><br />Import EDU.oswego.cs.dl.util.concurrent.*; <br /><br /><strong>public </strong><strong>class </strong>TestChannel { <br /><strong>final </strong>Channel msgQ = <strong>new </strong>LinkedQueue(); <em>//log信息队列 </em><br /><br /><strong>public </strong><strong>static </strong><strong>void </strong>main( <strong>String </strong>[] args) { <br />TestChannel tc = <strong>new </strong>TestChannel(); <br />For( <strong>int </strong>i = 0;i &lt; 10;i ++){ <br />Try{ <br />tc.serve(); <br /><strong>Thread </strong>.sleep(1000); <br />} <strong>catch </strong>( <strong>InterruptedException </strong>ie){ <br />} <br />} <br />} <br /><br /><strong>public </strong><strong>void </strong>serve() <strong>throws </strong><strong>InterruptedException </strong>{ <br /><strong>String </strong>status = doService(); <br /><em>//把doService()返回状态放入Channel，后台logger线程自动读取之 </em><br />msgQ.put(status); <br />} <br /><br /><strong>private </strong><strong>String </strong>doService() { <br /><em>// Do service here </em><br /><strong>return </strong>"service completed OK! " ; <br />} <br /><br /><strong>public </strong>TestChannel() { <em>// start background thread </em><br /><strong>Runnable </strong>logger = <strong>new </strong><strong>Runnable </strong>() { <br /><strong>public </strong><strong>void </strong>run() { <br /><strong>try </strong>{ <br /><strong>for </strong>(; ; ) <br /><strong>System </strong>.out.println( "Logger: " + msgQ.take()); <br />} <br /><strong>catch </strong>( <strong>InterruptedException </strong>ie) {} <br />} <br />}; <br /><strong>new </strong><strong>Thread </strong>(logger).start(); <br />} <br />} </p><p style="TEXT-INDENT: 2em"><strong>Excutor/ThreadFactory接口: </strong>把相关的线程创建/回收/维护/调度等工作封装起来，而让调用者只专心于具体任务的编码工作（即实现Runnable接口），不必显式创建Thread类实例就能异步执行任务。 <br />使用Executor还有一个好处，就是实现线程的“轻量级”使用。前面章节曾提到，即使我们实现了Runnable接口，要真正的创建线程，还是得通过new Thread()来完成，在这种情况下，Runnable对象(任务)和Thread对象(线程)是1对1的关系。如果任务多而简单，完全可以给每条线程配备一个任务队列，让Runnable对象(任务)和Executor对象变成n:1的关系。使用了Executor，我们可以把上面两种线程策略都封装到具体的Executor实现中，方便代码的实现和维护。 <br />具体的实现有: PooledExecutor，ThreadedExecutor，QueuedExecutor，FJTaskRunnerGroup等 <br />类关系图如下： 
</p><p align="center"><img src="http://www.7880.com/Upload/2006-06/4997C67EB89C0D400A4165AD9D004613.jpg" /></p>图6 Concurrent包Executor/ThreadFactory接口部分类关系图 <br />下面给出一段代码，使用PooledExecutor实现一个简单的多线程服务器 <br />代码5 
<p></p><p class="code"><strong>package </strong>org.javaresearch.j2seimproved.thread; <br /><strong>import </strong>java.net.*; <br /><strong>import </strong>EDU.oswego.cs.dl.util.concurrent.*; <br /><br /><strong>public </strong><strong>class </strong>TestExecutor { <br /><strong>public </strong><strong>static </strong><strong>void </strong>main( <strong>String </strong>[] args) { <br />PooledExecutor pool = <br /><strong>new </strong>PooledExecutor( <strong>new </strong>BoundedBuffer(10), 20); <br />pool.createThreads(4); <br /><strong>try </strong>{ <br /><strong>ServerSocket </strong>socket = <strong>new </strong><strong>ServerSocket </strong>(9999); <br /><strong>for </strong>(; ; ) { <br /><strong>final </strong>Socket connection = socket.accept(); <br />pool.execute( <strong>new </strong><strong>Runnable </strong>() { <br /><strong>public </strong><strong>void </strong>run() { <br /><strong>new </strong>Handler ().process(connection); <br />} <br />}); <br />} <br />} <br /><strong>catch </strong>( <strong>Exception </strong>e) {} <em>// die </em><br />} <br /><strong>static </strong><strong>class </strong>Handler { <br /><strong>void </strong>process( Socket s){ <br />} <br />} <br />} </p><p style="TEXT-INDENT: 2em">限于篇幅，这里只是蜻蜓点水式的介绍了Concurrent包，事实上还有相当多有用的接口和类没有提到，我们的配套光盘中附带了Concurrent包和源代码，感兴趣的读者可以自行分析。 </p></div>
		</div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/96868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-01-31 10:53 <a href="http://www.blogjava.net/jesson2005/articles/96868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>