﻿<?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-iNeo-文章分类-Java tech</title><link>http://www.blogjava.net/iNeo/category/5328.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 06:42:35 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 06:42:35 GMT</pubDate><ttl>60</ttl><item><title>杂谈架构和架构设计师</title><link>http://www.blogjava.net/iNeo/articles/24584.html</link><dc:creator>只牵这只狗</dc:creator><author>只牵这只狗</author><pubDate>Mon, 19 Dec 2005 03:08:00 GMT</pubDate><guid>http://www.blogjava.net/iNeo/articles/24584.html</guid><wfw:comment>http://www.blogjava.net/iNeo/comments/24584.html</wfw:comment><comments>http://www.blogjava.net/iNeo/articles/24584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iNeo/comments/commentRss/24584.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iNeo/services/trackbacks/24584.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;系统架构通俗的说起来就是系统的结构组织方式.原则上说, 架构只有好坏之分,而不存在有无的问题. 软件的体系架构可以直接体现为代码的类结构, 也可以表现为文档性的编码规范和全局约定等. 如果软件架构中能够抽象出一些稳定的元素, 那我们就可能得到一些所谓的框架代码. 一般业务架构是很难重用的, 目前常见的框架代码所描述的多半是与业务无关的技术架构. <BR>&nbsp;&nbsp; &nbsp;<BR>&nbsp;&nbsp;&nbsp; 良好的系统架构应该体现出应用本身的结构要求. 所谓各个为自己, 架构为大家. 只要各个局部符合规格, 应该由架构负责在合适的时刻按照合适的方式把它们组装在一些. 一个良好的架构中, 应该很少出现结构性的if语句, 不需要应用代码自己通过动态判断来定义某个特殊的触发时刻. 架构是一种规范, 当然也就是一种局限. 架构的可退化性是非常重要的, 否则一旦出现抽象泄露, 需要超出原有架构设计做出编码补充的时候, 往往无法将代码自然的融入原有的框架结构, 则整个框架出现大面积的失效情况. 而有的时候更糟糕的情况是一些关键性的资源处在原有技术架构的私有控制之中, 我们为了克服架构限制不得不采用各种trick来hack原有框架, 造成错误的累加和传播, 而补丁的补丁是最难维护的.<BR><BR>&nbsp;&nbsp;&nbsp; 架构问题并不是一成不变的. 在一些情形下无关紧要的问题在另一种情形下可能会成为灾难性的架构问题. 例如在多层B/S架构下, 如果现在要求为每一个表增加一个对应的历史表, 并对其进行查看和维护操作. 为了最大限度的重用代码, 这要求我们的多层结构中的每一层都能够参数化, 这样我们才能用同样的代码处理不同的数据表. 如果我们的money很足, 小弟够多, 有足够的人月砸上去, 那么我们完全可以把业务表和历史表分开处理, 但如果反之,我们就会遇到一个典型的架构问题. <BR><BR>&nbsp;&nbsp;&nbsp; 架构师未必有自己的框架, 因为设计不等价于创造, 架构师只要知道如何把系统中的各种元素按照可行的方式组装在一起就可以了. 但是一个架构设计是非常依赖于我们所能采用的技术手段的, 当现有各种可用的技术元素都无法满足我们的需求的时候, 某些架构师可能会选择创造一种技术元素. 当然, 创造是艰难的, 它所要求的甚至是不同的技能. Sun的Green项目创造了java语言, 从而开启了一个伟大的时代, 这绝对不会是大多数架构设计师的选择(有趣的是,Green项目本身失败了). EJB现在还有多少人在真正使用, 想想当年多少架构师在吹嘘这些东西. 他们对于技术的把握真的就那么幼稚吗? 架构设计并不是凭空出现的, 当时可选的东西就是如此, 而spring和hibernate这些都不属于架构设计本身的内容.它们是一种创造.<BR>&nbsp;&nbsp; &nbsp;<BR>&nbsp;&nbsp;&nbsp; 架构师未必是团队的领导者. 确实,他的工作类似于编剧, 负责执行的一般是导演. 事实上,一个建筑设计师是极少直接领导一个工程队的.架构师也未必比高级程序员要高明, 他们负责的是不同的内容. 至于产品的"商标及商标的相关元素"和"技术市场架构"等也不属于架构师的工作范畴, 他不能去抢产品经理的饭碗. 当然,在国内的现实情况下, 很多所谓的架构师所做的最重要的工作可能是公关工作, 向客户秀出所谓的理念, 与实际开发是不搭嘎的.&nbsp;&nbsp;&nbsp; &nbsp;<BR>&nbsp;&nbsp; &nbsp;<BR>&nbsp;&nbsp;&nbsp; 理论上说, 架构师可以不是编程的强者, 也可以不决定一些具体数据结构的选择, 但他不能不了解各种技术抉择潜在的影响. 这就如同一个建筑设计师可以不精通工程力学,但是他不能愚蠢到藐视重力, 设计出倒三角式的大厦. 与建筑不同的是, 在软件中我们所面临的不是一种"凝固的艺术", 我们无法以完全静态的方式理解代码,而必须在头脑中把它们运行起来. 架构师应该写下一些实际的代码, 以检验各个接口的可配合性并获得对于代码结构的直接感觉. 实际上, 按照现在软件业的成熟度, 一般我们无法实现建筑中建筑设计师与土木工程师的分工, 很多时候软件架构师都需要直接面对实现的细节. 如果组内缺乏非常强悍的coder, 有编程能力的架构师亲自操刀实现关键性代码的时候也是很多的.<BR><BR>&nbsp;&nbsp;&nbsp; 架构师必须有经验, 但他所依赖的不能只是经验. 只要算一算架构师的年纪, 就会知道以他们在这个世界上的存在时间, 并不足以使得他们经历各种技术细节. 架构设计更多的是依赖我们对于系统结构原理的理解, 而经验可以让我们规避那些原理失效的地方(例如系统级bug). 君子非异能也, 善假于物也. 很多时候,我们更应该从有经验的朋友或者技术支持那里搜集技术细节, 以确保它们能够满足我们在架构上的原理性需求. Know Why而不仅仅是Know How是非常重要的. 一个农民发明家也许可以得到某个巧妙的机械设计, 但是没有系统的掌握工程力学, 他们是无法去开发精密的导弹控制系统的.当然, 软件开发还处在非常原始的阶段, 掌握一些设计原理和设计模式多半也不过是五十步笑百步而已, 经验的地位是无可替代的.<BR><BR>&nbsp;&nbsp;&nbsp; 架构师不是预言家. 在多变的业务环境中, 架构师的目标不应该是预测到所有的变化可能, 并把它们表达到系统架构中. 这个世界上不乏一些耗资数十亿,设计三四年,但最终每个谈到它的人都要说一句shit的产品开发项目. 架构设计所能做到的最好的程度是自然的标注出系统的结构边界,成功的delay各种技术抉择.<BR><BR>&nbsp;&nbsp;&nbsp; 架构师不是超人, 他所考虑的东西也许要远一些, 所需要平衡的利益也许要多一些, 但是单独一个人是无法对整个产品或者项目的成败负责的. 如果ThoughtWorks的Martin Follower来处理国内的某些项目, 我估计他会死得很难看.架构师也是人, 也会犯错误,甚至是很低级的错误, 而每个人都会有一些独特的想法. 经历的多了, 你就会回归到终极的认识, 一切都只是浮云, 只有money才是硬道理. <BR>
<P class=postfoot align=right>2005-12-18 17:35&nbsp;作者:&nbsp;<A class=clsSubText HREF="/canonical/"><FONT color=#000099>canonical</FONT></A><A class=CommentLink HREF="/canonical/archive/2005/12/18/24508.html#post"><FONT color=#000000>【评论:3】</FONT></A>【阅读:97】</P><img src ="http://www.blogjava.net/iNeo/aggbug/24584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iNeo/" target="_blank">只牵这只狗</a> 2005-12-19 11:08 <a href="http://www.blogjava.net/iNeo/articles/24584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>精华:Rundll32.exe文件详解</title><link>http://www.blogjava.net/iNeo/articles/24001.html</link><dc:creator>只牵这只狗</dc:creator><author>只牵这只狗</author><pubDate>Thu, 15 Dec 2005 04:04:00 GMT</pubDate><guid>http://www.blogjava.net/iNeo/articles/24001.html</guid><wfw:comment>http://www.blogjava.net/iNeo/comments/24001.html</wfw:comment><comments>http://www.blogjava.net/iNeo/articles/24001.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iNeo/comments/commentRss/24001.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iNeo/services/trackbacks/24001.html</trackback:ping><description><![CDATA[<P>winexec(Pchar('StrCommand'),sw_Show); <BR>其中"StrCommand"代表以下命令之一(使用Windows中的运行不要加引号)： <BR>"rundll32 shell32,Control_RunDLL" - 运行控制面板 <BR>"rundll32 shell32,OpenAs_RunDLL" - 打开"打开方式"窗口 <BR>"rundll32 shell32,ShellAboutA Info-Box" - 打开"关于"窗口 <BR>"rundll32 shell32,Control_RunDLL desk.cpl" - 打开"显示属性"窗口 <BR>"rundll32 user,cascadechildwindows" - 层叠全部窗口 <BR>"rundll32 user,tilechildwindows" - 最小化所有的子窗口 <BR>"rundll32 user,repaintscreen" - 刷新桌面 <BR>"rundll32 shell,shellexecute Explorer" - 重新运行Windows Explorer <BR>"rundll32 keyboard,disable" - 锁写键盘 <BR>"rundll32 mouse,disable" - 让鼠标失效 <BR>"rundll32 user,swapmousebutton" - 交换鼠标按钮 <BR>"rundll32 user,setcursorpos" - 设置鼠标位置为(0,0) <BR>"rundll32 user,wnetconnectdialog" - 打开"映射网络驱动器"窗口 <BR>"rundll32 user,wnetdisconnectdialog" - 打开"断开网络驱动器"窗口 <BR>"rundll32 user,disableoemlayer" - 显示BSOD窗口, (BSOD) = Blue Screen Of <BR>Death, 即蓝屏 <BR>"rundll32 diskcopy,DiskCopyRunDll" - 打开磁盘复制窗口 <BR>"rundll32 rnaui.dll,RnaWizard" - 运行"Internet连接向导", <BR>如果加上参数"/1"则为silent模式 <BR>"rundll32 shell32,SHFormatDrive" - 打开"格式化磁盘(A)"窗口 <BR>"rundll32 shell32,SHExitWindowsEx -1" - 冷启动Windows Explorer <BR>"rundll32 shell32,SHExitWindowsEx 1" - 关机 <BR>"rundll32 shell32,SHExitWindowsEx 0" - 退当前用户 <BR>"rundll32 shell32,SHExitWindowsEx 2" Windows9x 快速重启 <BR>"rundll32 krnl386.exe,exitkernel" - 强行退出Windows 9x(无确认) <BR>"rundll rnaui.dll,RnaDial "MyConnect" - 运行"网络连接"对话框 <BR>"rundll32 msprint2.dll,RUNDLL_PrintTestPage" - 选择打印机和打印测试页 <BR>"rundll32 user,setcaretblinktime" - 设置光标闪烁速度 <BR>"rundll32 user, setdoubleclicktime" - 测试鼠标双击速度 <BR>"rundll32 sysdm.cpl,InstallDevice_Rundll" - 搜索非PnP设备 <BR>　控制面板中的各项功能 </P>
<P>　winexec('rundll32.exe shell32.dll, Control_RunDLL', 9); <BR>　{辅助选项 属性-键盘} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 1', 9); <BR>　{辅助选项 属性-声音} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 2', 9); <BR>　{辅助选项 属性-显示} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 3', 9); <BR>　{辅助选项 属性-鼠标} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 4', 9); <BR>　{辅助选项 属性-常规} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 5', 9); <BR>　{添加/删除程序 属性-安装/卸载} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 1', 9); <BR>　{添加/删除程序 属性-Windows安装程序} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 2', 9); <BR>　{添加/删除程序 属性-启动盘} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 3', 9); <BR>　{显示 属性-背景} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 0', 9);　 <BR>　{显示 属性-屏幕保护程序} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 1', 9); <BR>　{显示 属性-外观} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 2', 9); <BR>　{显示 属性-设置} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 3', 9); <BR>　{Internet 属性-常规} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 0', <BR>9); <BR>　{Internet 属性-安全} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 1', <BR>9); <BR>　{Internet 属性-内容} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 2', <BR>9); <BR>　{Internet 属性-连接} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 3', <BR>9); <BR>　{Internet 属性-程序} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 4', <BR>9); <BR>　{Internet 属性-高级} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 5', <BR>9); <BR>　{区域设置 属性-区域设置} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 0', 9); <BR>　{区域设置 属性-数字} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 1', 9); <BR>　{区域设置 属性-货币} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 2', 9); <BR>　{区域设置 属性-时间} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 3', 9); <BR>　{区域设置 属性-日期} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 4', 9); <BR>　 <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Joy.cpl, 0', 9); </P>
<P><BR>winexec('rundll32.exe shell32.dll, Control_RunDLL Joy.cpl, 1', 9); <BR>　{鼠标 属性} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Main.cpl', 9); <BR>　{多媒体 属性-音频} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 0', 9); <BR>　{多媒体 属性-视频} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 1', 9); <BR>　{多媒体 属性-MIDI} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 2', 9); <BR>　{多媒体 属性-CD音乐} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 3', 9); <BR>　{多媒体 属性-设备} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 4', 9); <BR>　{调制解调器 属性} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Modem.cpl', 9); <BR>　 <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Netcpl.cpl', 9); <BR>　{密码 属性} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Password.cpl', 9); <BR>　{扫描仪与数字相机 属性} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Sticpl.cpl', 9); <BR>　{系统 属性-常规} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 0', 9); <BR>　{系统 属性-设备管理器} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 1', 9); <BR>　{系统 属性-硬件配置文件} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 2', 9); <BR>　{系统 属性-性能} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 3', 9); <BR>　{日期/时间 属性} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL timedate.cpl', 9); <BR>　{电源管理 属性} <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Powercfg.cpl', 9); <BR>　 <BR>　winexec('rundll32.exe shell32.dll, Control_RunDLL Telephon.cpl', 9); <BR>关于调用后的判断处理建议： <BR>先声明一个cardinal类型的变量RtnCardinal获取返回值进行判断如： <BR>RtnCardinal := winexec('rundll32.exe shell32.dll, Control_RunDLL <BR>Telephon.cpl', 9); <BR>　返回值 可能原因 <BR>　0 程序超出内存　 <BR>ERROR_BAD_FORMAT 程序为一个非法的Win32.EXE程序 <BR>ERROR_FILE_NOT_FOUND 指定文件没找到　 <BR>ERROR_PATH_NOT_FOUND 指定路径没找到 </P>
<P>使用方法： <BR>点击“开始－程式－Ms－Dos方式”，进入Dos视窗，然後键入"rundll32.exe <BR>user.exe,restartwindows"，再按下回车键，这时你将看到，机器被重启了！怎么样，是不是很有趣？ <BR>　　当然，Rundll的功能绝不仅仅是重启你的机器。其实，Rundll者，顾名思义，执行Dll也，它的功能就是以命令列的方式呼叫Windows的动态链结库，Rundll32.exe与Rundll.exe的区别就在於前者是呼叫32位的链结库，而後者是运用於16位的链结库，它们的命令格式是： <BR>　　RUNDLL.EXE ，， <BR>　　这里要注意三点：1.Dll档案名中不能含有空格，比如该档案位於c:Program <BR>Files目录，你要把这个路径改成c:Progra～1；2.Dll档案名与Dll入口点间的逗号不能少，否则程式将出错并且不会给出任何资讯！3.这是最重要的一点：Rundll不能用来呼叫含返回值参数的Dll，例如Win32API中的GetUserName(),GetTextFace()等。在Visual <BR>Basic中，提供了一条执行外部程式的指令Shell,格式为： <BR>　　Shell “命令列” <BR>　　如果能配合Rundll32.exe用好Shell指令，会使您的VB程式拥有用其他方法难以甚至无法实现的效果：仍以重启为例，传统的方法需要你在VB工程中先建立一个模组，然後写入WinAPI的声明，最後才能在程式中呼叫。而现在只需一句: <BR>　　Shell “rundll32.exe <BR>user.exe,restartwindows”就搞定了！是不是方便多了？ <BR>　　实际上，Rundll32.exe在呼叫各种Windows控制面板和系统选项方面有著独特的优势。下面，我就将本人在因特网上收集的有关Rundll的指令列举如下（很有用的，能省去你很多呼叫Windows <BR>API的时间！！），供大家在程式设计中引用： <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL <BR>　　功能: 显示控制面板 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1 <BR>　　功能: 显示“控制面板－辅助选项－键盘”选项视窗 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2 <BR>　　功能: 显示“控制面板－辅助选项－声音”选项视窗 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3 <BR>　　功能: 显示“控制面板－辅助选项－显示”选项视窗 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4 <BR>　　功能: 显示“控制面板－辅助选项－滑鼠”选项视窗 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5 <BR>　　功能: 显示“控制面板－辅助选项－传统”选项视窗 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1 <BR>　　功能: 执行“控制面板－添加新硬体”向导。 <BR>　　命令列: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter <BR>　　功能: 执行“控制面板－添加新印表机”向导。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,1 <BR>　　功能: 显示 “控制面板－添加/删除程式－安装/卸载” 面板。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,2 <BR>　　功能: 显示 “控制面板－添加/删除程式－安装Windows” 面板。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,3 <BR>　　功能: 显示 “控制面板－添加/删除程式－启动盘” 面板。 <BR>　　命令列: rundll32.exe syncui.dll,Briefcase_Create <BR>　　功能: 在桌面上建立一个新的“我的公文包”。 <BR>　　命令列: rundll32.exe diskcopy.dll,DiskCopyRunDll <BR>　　功能: 显示复制软碟视窗 <BR>　　命令列: rundll32.exe apwiz.cpl,NewLinkHere ％1 <BR>　　功能: <BR>显示“建立快捷方式”的对话框，所建立的快捷方式的位置由％1参数决定。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,0 <BR>　　功能: 显示“日期与时间”选项视窗。 </P>
<P><BR>命令列: rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,1 <BR>　　功能: 显示“时区”选项视窗。 <BR>　　命令列: rundll32.exe rnaui.dll,RnaDial [某个拨号连接的名称] <BR>　　功能: <BR>显示某个拨号连接的拨号视窗。如果已经拨号连接，则显示目前的连接状态的视窗。 <BR>　　命令列: rundll32.exe rnaui.dll,RnaWizard <BR>　　功能: 显示“新建拨号连接”向导的视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0 <BR>　　功能: 显示“显示属性－背景”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1 <BR>　　功能: 显示“显示属性－萤屏保护”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2 <BR>　　功能: 显示“显示属性－外观”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3 <BR>　　功能: 显示显示“显示属性－属性”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL FontsFolder <BR>　　功能: 显示Windows的“字体”档案夹。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @3 <BR>　　功能: 同样是显示Windows的“字体”档案夹。 <BR>　　命令列: rundll32.exe shell32.dll,SHformatDrive <BR>　　功能: 显示格式化软碟对话框。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL joy.cpl,,0 <BR>　　功能: 显示“控制面板－游戏控制器－一般”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL joy.cpl,,1 <BR>　　功能: 显示“控制面板－游戏控制器－进阶”选项视窗。 <BR>　　命令列: rundll32.exe mshtml.dll,PrintHTML (HTML文档) <BR>　　功能: 列印HTML文档。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mlcfg32.cpl <BR>　　功能: 显示Microsoft Exchange一般选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @0 <BR>　　功能: 显示“控制面板－滑鼠” 选项 。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1 <BR>　　功能: 显示 “控制面板－键盘属性－速度”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1,,1 <BR>　　功能: 显示 “控制面板－键盘属性－语言”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @2 <BR>　　功能: 显示Windows“印表机”档案夹。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @3 <BR>　　功能: 显示Windows“字体”档案夹。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @4 <BR>　　功能: 显示“控制面板－输入法属性－输入法”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add <BR>　　功能: 执行“添加新调制解调器”向导。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,0 <BR>　　功能: 显示“控制面板－多媒体属性－音频”属性页。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1 <BR>　　功能: 显示“控制面板－多媒体属性－视频”属性页。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,2 <BR>　　功能: 显示“控制面板－多媒体属性－MIDI”属性页。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,3 <BR>　　功能: 显示“控制面板－多媒体属性－CD音乐”属性页。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,4 <BR>　　功能: 显示“控制面板－多媒体属性－设备”属性页。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl @1 <BR>　　功能: 显示“控制面板－声音”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl <BR>　　功能: 显示“控制面板－网路”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL odbccp32.cpl <BR>　　功能: 显示ODBC32资料管理选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,OpenAs_RunDLL <BR>功能: 显示指定档案(drive:pathfilename)的“打开方式”对话框。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL password.cpl <BR>　　功能: 显示“控制面板－密码”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL powercfg.cpl <BR>　　功能: 显示“控制面板－电源管理属性”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL <BR>PrintersFolder <BR>　　功能: 显示Windows“印表机”档案夹。(同rundll32.exe <BR>shell32.dll,Control_RunDLL main.cpl @2) <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,0 <BR>　　功能: 显示“控制面板－区域设置属性－区域设置”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,1 <BR>　　功能: 显示“控制面板－区域设置属性－数字”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,2 <BR>　　功能: 显示“控制面板－区域设置属性－货币”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,3 <BR>　　功能: 显示“控制面板－区域设置属性－时间”选项视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,4 <BR>　　功能: 显示“控制面板－区域设置属性－日期”选项视窗。 <BR>　　命令列: rundll32.exe desk.cpl,InstallScreenSaver [萤屏保护档案名] <BR>　　功能: <BR>将指定的萤屏保护档案设置为Windows的屏保，并显示萤屏保护属性视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,0 <BR>　　功能: 显示“控制面板－系统属性－传统”属性视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,1 <BR>　　功能: 显示“控制面板－系统属性－设备管理器”属性视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,2 <BR>　　功能: 显示“控制面板－系统属性－硬体配置档案”属性视窗。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,3 <BR>　　功能: 显示“控制面板－系统属性－性能”属性视窗。 <BR>　　命令列: rundll32.exe user.exe,restartwindows <BR>　　功能: 强行关闭所有程式并重启机器。 <BR>　　命令列: rundll32.exe user.exe,exitwindows <BR>　　功能: 强行关闭所有程式并关机。 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL telephon.cpl <BR>　　功能: 显示“拨号属性”选项视窗 <BR>　　命令列: rundll32.exe shell32.dll,Control_RunDLL themes.cpl <BR>　　功能: 显示“桌面主旨”选项面板 <BR>　　当然，不止是VisualBasic，象Delphi.VisualC＋＋等其他程式设计语言也可以通过呼叫外部命令的方法来使用Rundll的这些功能，具体方法这里就不再详细叙述了。灵活的使用Rundll,一定会使你的程式设计轻轻松松，达到事半功倍的效果</P><img src ="http://www.blogjava.net/iNeo/aggbug/24001.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iNeo/" target="_blank">只牵这只狗</a> 2005-12-15 12:04 <a href="http://www.blogjava.net/iNeo/articles/24001.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java多线程设计模式</title><link>http://www.blogjava.net/iNeo/articles/23754.html</link><dc:creator>只牵这只狗</dc:creator><author>只牵这只狗</author><pubDate>Wed, 14 Dec 2005 00:54:00 GMT</pubDate><guid>http://www.blogjava.net/iNeo/articles/23754.html</guid><wfw:comment>http://www.blogjava.net/iNeo/comments/23754.html</wfw:comment><comments>http://www.blogjava.net/iNeo/articles/23754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iNeo/comments/commentRss/23754.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iNeo/services/trackbacks/23754.html</trackback:ping><description><![CDATA[<H2>java多线程设计模式 </H2>
<P>java语言已经内置了多线程支持，所有实现Runnable接口的类都可被启动一个新线程，新线程会执行该实例的run()方法，当run()方法执行完毕后，线程就结束了。一旦一个线程执行完毕，这个实例就不能再重新启动，只能重新生成一个新实例，再启动一个新线程。</P>
<P>Thread类是实现了Runnable接口的一个实例，它代表一个线程的实例，并且，启动线程的唯一方法就是通过Thread类的start()实例方法：</P>
<P>Thread t = new Thread();<BR>t.start();</P>
<P>start()方法是一个native方法，它将启动一个新线程，并执行run()方法。Thread类默认的run()方法什么也不做就退出了。注意：直接调用run()方法并不会启动一个新线程，它和调用一个普通的java方法没有什么区别。</P>
<P>因此，有两个方法可以实现自己的线程：</P>
<P>方法1：自己的类extend Thread，并复写run()方法，就可以启动新线程并执行自己定义的run()方法。例如：</P>
<P>public class MyThread extends Thread {<BR>&nbsp;&nbsp;&nbsp; public run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("MyThread.run()");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>在合适的地方启动线程：new MyThread().start();</P>
<P>方法2：如果自己的类已经extends另一个类，就无法直接extends Thread，此时，必须实现一个Runnable接口：</P>
<P>public class MyThread extends OtherClass implements Runnable {<BR>&nbsp;&nbsp;&nbsp; public run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("MyThread.run()");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>为了启动MyThread，需要首先实例化一个Thread，并传入自己的MyThread实例：</P>
<P>MyThread myt = new MyThread();<BR>Thread t = new Thread(myt);<BR>t.start();</P>
<P>事实上，当传入一个Runnable target参数给Thread后，Thread的run()方法就会调用target.run()，参考JDK源代码：</P>
<P>public void run() {<BR>&nbsp;&nbsp;&nbsp; if (target != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; target.run();<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>线程还有一些Name, ThreadGroup, isDaemon等设置，由于和线程设计模式关联很少，这里就不多说了。<BR></P>
<P>由于同一进程内的多个线程共享内存空间，在Java中，就是共享实例，当多个线程试图同时修改某个实例的内容时，就会造成冲突，因此，线程必须实现共享互斥，使多线程同步。</P>
<P>最简单的同步是将一个方法标记为synchronized，对同一个实例来说，任一时刻只能有一个synchronized方法在执行。当一个方法正在执行某个synchronized方法时，其他线程如果想要执行这个实例的任意一个synchronized方法，都必须等待当前执行 synchronized方法的线程退出此方法后，才能依次执行。</P>
<P>但是，非synchronized方法不受影响，不管当前有没有执行synchronized方法，非synchronized方法都可以被多个线程同时执行。</P>
<P>此外，必须注意，只有同一实例的synchronized方法同一时间只能被一个线程执行，不同实例的synchronized方法是可以并发的。例如，class A定义了synchronized方法sync()，则不同实例a1.sync()和a2.sync()可以同时由两个线程来执行。<BR></P>
<P>多线程同步的实现最终依赖锁机制。我们可以想象某一共享资源是一间屋子，每个人都是一个线程。当A希望进入房间时，他必须获得门锁，一旦A获得门锁，他进去后就立刻将门锁上，于是B,C,D...就不得不在门外等待，直到A释放锁出来后，B,C,D...中的某一人抢到了该锁（具体抢法依赖于 JVM的实现，可以先到先得，也可以随机挑选），然后进屋又将门锁上。这样，任一时刻最多有一人在屋内（使用共享资源）。</P>
<P>Java语言规范内置了对多线程的支持。对于Java程序来说，每一个对象实例都有一把“锁”，一旦某个线程获得了该锁，别的线程如果希望获得该锁，只能等待这个线程释放锁之后。获得锁的方法只有一个，就是synchronized关键字。例如：</P>
<P>public class SharedResource {<BR>&nbsp;&nbsp;&nbsp; private int count = 0;<BR><BR>&nbsp;&nbsp;&nbsp; public int getCount() { return count; }<BR><BR>&nbsp;&nbsp;&nbsp; public <FONT color=#ff0000><STRONG>synchronized</STRONG></FONT> void setCount(int count) { this.count = count; }<BR><BR>}</P>
<P>同步方法public synchronized void setCount(int count) { this.count = count; } 事实上相当于：</P>
<P>public&nbsp;void setCount(int count) {<BR>&nbsp;&nbsp;&nbsp; <STRONG><FONT color=#339966>synchronized(this) { // 在此获得this锁<BR></FONT><FONT color=#ff0000>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.count = count;</FONT></STRONG><BR>&nbsp;&nbsp;&nbsp; <FONT color=#339966><STRONG>} // 在此释放this锁<BR></STRONG></FONT>}</P>
<P>红色部分表示需要同步的代码段，该区域为“危险区域”，如果两个以上的线程同时执行，会引发冲突，因此，要更改SharedResource的内部状态，必须先获得SharedResource实例的锁。</P>
<P>退出synchronized块时，线程拥有的锁自动释放，于是，别的线程又可以获取该锁了。</P>
<P>为了提高性能，不一定要锁定this，例如，SharedResource有两个独立变化的变量：</P>
<P>public class SharedResouce {<BR>&nbsp;&nbsp;&nbsp; private int a = 0;<BR>&nbsp;&nbsp;&nbsp; private int b = 0;<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void setA(int a) { this.a = a; }<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void setB(int b) { this.b = b; }<BR>}</P>
<P>若同步整个方法，则setA()的时候无法setB()，setB()时无法setA()。为了提高性能，可以使用不同对象的锁：</P>
<P>public class SharedResouce {<BR>&nbsp;&nbsp;&nbsp; private int a = 0;<BR>&nbsp;&nbsp;&nbsp; private int b = 0;<BR>&nbsp;&nbsp;&nbsp; private Object sync_a = new Object();<BR>&nbsp;&nbsp;&nbsp; private Object sync_b = new Object();<BR><BR>&nbsp;&nbsp;&nbsp; public void setA(int a) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#339966><STRONG>synchronized(sync_a) {<BR></STRONG></FONT>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.a = a;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void setB(int b) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#339966><STRONG>synchronized(sync_b) {<BR></STRONG></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.b = b;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR></P>
<P>通常，多线程之间需要协调工作。例如，浏览器的一个显示图片的线程displayThread想要执行显示图片的任务，必须等待下载线程 downloadThread将该图片下载完毕。如果图片还没有下载完，displayThread可以暂停，当downloadThread完成了任务后，再通知displayThread“图片准备完毕，可以显示了”，这时，displayThread继续执行。</P>
<P>以上逻辑简单的说就是：如果条件不满足，则等待。当条件满足时，等待该条件的线程将被唤醒。在Java中，这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如：</P>
<P>synchronized(obj) {<BR>&nbsp;&nbsp;&nbsp; while(!condition) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.<STRONG><FONT color=#0000ff>wait()</FONT></STRONG>;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; obj.doSomething();<BR>}</P>
<P>当线程A获得了obj锁后，发现条件condition不满足，无法继续下一处理，于是线程A就wait()。</P>
<P>在另一线程B中，如果B更改了某些条件，使得线程A的condition条件满足了，就可以唤醒线程A：</P>
<P>synchronized(obj) {<BR>&nbsp;&nbsp;&nbsp; condition = true;<BR>&nbsp;&nbsp;&nbsp; obj.notify();<BR>}</P>
<P><STRONG><FONT color=#ff0000>需要注意的概念是：</FONT></STRONG></P>
<P># 调用obj的wait(), notify()方法前，必须获得obj锁，也就是必须写在synchronized(obj) {...} 代码段内。</P>
<P># 调用obj.wait()后，线程A就释放了obj的锁，否则线程B无法获得obj锁，也就无法在synchronized(obj) {...} 代码段内唤醒A。</P>
<P># 当obj.wait()方法返回后，线程A需要再次获得obj锁，才能继续执行。</P>
<P># 如果A1,A2,A3都在obj.wait()，则B调用obj.notify()只能唤醒A1,A2,A3中的一个（具体哪一个由JVM决定）。</P>
<P># obj.notifyAll()则能全部唤醒A1,A2,A3，但是要继续执行obj.wait()的下一条语句，必须获得obj锁，因此，A1,A2,A3只有一个有机会获得锁继续执行，例如A1，其余的需要等待A1释放obj锁之后才能继续执行。</P>
<P># 当B调用obj.notify/notifyAll的时候，B正持有obj锁，因此，A1,A2,A3虽被唤醒，但是仍无法获得obj锁。直到B退出synchronized块，释放obj锁后，A1,A2,A3中的一个才有机会获得锁继续执行。</P><BR>
<P>前面讲了wait/notify机制，Thread还有一个sleep()静态方法，它也能使线程暂停一段时间。sleep与wait的不同点是： sleep并不释放锁，并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。</P>
<P>但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态，从而使线程立刻抛出InterruptedException。</P>
<P>如果线程A希望立即结束线程B，则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在 wait/sleep/join，则线程B会立刻抛出InterruptedException，在catch() {} 中直接return即可安全地结束线程。</P>
<P>需要注意的是，InterruptedException是线程自己从内部抛出的，并不是interrupt()方法抛出的。对某一线程调用 interrupt()时，如果该线程正在执行普通的代码，那么该线程根本就不会抛出InterruptedException。但是，一旦该线程进入到 wait()/sleep()/join()后，就会立刻抛出InterruptedException。</P><BR>
<P>GuardedSuspention模式主要思想是：</P>
<P>当条件不满足时，线程等待，直到条件满足时，等待该条件的线程被唤醒。</P>
<P>我们设计一个客户端线程和一个服务器线程，客户端线程不断发送请求给服务器线程，服务器线程不断处理请求。当请求队列为空时，服务器线程就必须等待，直到客户端发送了请求。</P>
<P>先定义一个请求队列：Queue</P>
<P>package com.crackj2ee.thread;<BR><BR>import java.util.*;<BR><BR>public class Queue {<BR>&nbsp;&nbsp;&nbsp; private List queue = new LinkedList();<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized Request <FONT color=#ff0000>getRequest</FONT>() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#0000ff><STRONG>while(queue.size()==0) {<BR></STRONG></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.wait();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Request)queue.remove(0);<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void <FONT color=#ff0000>putRequest</FONT>(Request request) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queue.add(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#993300><STRONG>this.notifyAll();</STRONG></FONT><BR>&nbsp;&nbsp;&nbsp; }<BR><BR>}</P>
<P>蓝色部分就是服务器线程的等待条件，而客户端线程在放入了一个request后，就使服务器线程等待条件满足，于是唤醒服务器线程。</P>
<P>客户端线程：ClientThread</P>
<P>package com.crackj2ee.thread;<BR><BR>public class ClientThread extends Thread {<BR>&nbsp;&nbsp;&nbsp; private Queue queue;<BR>&nbsp;&nbsp;&nbsp; private String clientName;<BR><BR>&nbsp;&nbsp;&nbsp; public ClientThread(Queue queue, String clientName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.queue = queue;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.clientName = clientName;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public String toString() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "[ClientThread-" + clientName + "]";<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;100; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Request request = new Request("" + (long)(Math.random()*10000));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(this + " send request: " + request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG><FONT color=#ff0000>queue.putRequest(request);<BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep((long)(Math.random() * 10000 + 1000));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(this + " shutdown.");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>服务器线程：ServerThread</P>
<P>package com.crackj2ee.thread;<BR>public class ServerThread extends Thread {<BR>&nbsp;&nbsp;&nbsp; private boolean stop = false;<BR>&nbsp;&nbsp;&nbsp; private Queue queue;<BR><BR>&nbsp;&nbsp;&nbsp; public ServerThread(Queue queue) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.queue = queue;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public void shutdown() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stop = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.interrupt();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.join();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(!stop) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#ff0000><STRONG>Request request = queue.getRequest();<BR></STRONG></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("[ServerThread] handle request: " + request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(2000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("[ServerThread] shutdown.");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>服务器线程在红色部分可能会阻塞，也就是说，Queue.getRequest是一个阻塞方法。这和java标准库的许多IO方法类似。</P>
<P>最后，写一个Main来启动他们：</P>
<P>package com.crackj2ee.thread;<BR><BR>public class Main {<BR><BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Queue queue = new Queue();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServerThread server = new ServerThread(queue);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#ff0000><STRONG>server.start();<BR></STRONG></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClientThread[] clients = new ClientThread[5];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;clients.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clients[i] = new ClientThread(queue, ""+i);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT color=#ff0000><STRONG>clients[i].start();<BR></STRONG></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(100000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.shutdown();<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>我们启动了5个客户端线程和一个服务器线程，运行结果如下：</P>
<P>[ClientThread-0] send request: Request-4984<BR>[ServerThread] handle request: Request-4984<BR>[ClientThread-1] send request: Request-2020<BR>[ClientThread-2] send request: Request-8980<BR>[ClientThread-3] send request: Request-5044<BR>[ClientThread-4] send request: Request-548<BR>[ClientThread-4] send request: Request-6832<BR>[ServerThread] handle request: Request-2020<BR>[ServerThread] handle request: Request-8980<BR>[ServerThread] handle request: Request-5044<BR>[ServerThread] handle request: Request-548<BR>[ClientThread-4] send request: Request-1681<BR>[ClientThread-0] send request: Request-7859<BR>[ClientThread-3] send request: Request-3926<BR>[ServerThread] handle request: Request-6832<BR>[ClientThread-2] send request: Request-9906<BR>......</P>
<P>可以观察到ServerThread处理来自不同客户端的请求。</P>
<P><STRONG><FONT color=#ff0000>思考</FONT></STRONG></P>
<P>Q: 服务器线程的wait条件while(queue.size()==0)能否换成if(queue.size()==0)?</P>
<P>A: 在这个例子中可以，因为服务器线程只有一个。但是，如果服务器线程有多个（例如Web应用程序有多个线程处理并发请求，这非常普遍），就会造成严重问题。</P>
<P>Q: 能否用sleep(1000)代替wait()?</P>
<P>A: 绝对不可以。sleep()不会释放锁，因此sleep期间别的线程根本没有办法调用getRequest()和putRequest()，导致所有相关线程都被阻塞。</P>
<P>Q: (Request)queue.remove(0)可以放到synchronized() {}块外面吗？</P>
<P>A: 不可以。因为while()是测试queue，remove()是使用queue，两者是一个原子操作，不能放在synchronized外面。</P>
<P><STRONG><FONT color=#ff0000>总结</FONT></STRONG></P>
<P>多线程设计看似简单，实际上必须非常仔细地考虑各种锁定/同步的条件，稍不小心，就可能出错。并且，当线程较少时，很可能发现不了问题，一旦问题出现又难以调试。</P>
<P>所幸的是，已有一些被验证过的模式可以供我们使用，我们会继续介绍一些常用的多线程设计模式。<BR></P>
<P>前面谈了多线程应用程序能极大地改善用户相应。例如对于一个Web应用程序，每当一个用户请求服务器连接时，服务器就可以启动一个新线程为用户服务。</P>
<P>然而，创建和销毁线程本身就有一定的开销，如果频繁创建和销毁线程，CPU和内存开销就不可忽略，垃圾收集器还必须负担更多的工作。因此，线程池就是为了避免频繁创建和销毁线程。</P>
<P>每当服务器接受了一个新的请求后，服务器就从线程池中挑选一个等待的线程并执行请求处理。处理完毕后，线程并不结束，而是转为阻塞状态再次被放入线程池中。这样就避免了频繁创建和销毁线程。</P>
<P>Worker Pattern实现了类似线程池的功能。首先定义Task接口：</P>
<P>package com.crackj2ee.thread;<BR>public interface Task {<BR>&nbsp;&nbsp;&nbsp; void <STRONG><FONT color=#0000ff>execute</FONT></STRONG>();<BR>}</P>
<P>线程将负责执行execute()方法。注意到任务是由子类通过实现execute()方法实现的，线程本身并不知道自己执行的任务。它只负责运行一个耗时的execute()方法。</P>
<P>具体任务由子类实现，我们定义了一个CalculateTask和一个TimerTask：</P>
<P>// CalculateTask.java<BR>package com.crackj2ee.thread;<BR>public class CalculateTask implements Task {<BR>&nbsp;&nbsp;&nbsp; private static int count = 0;<BR>&nbsp;&nbsp;&nbsp; private int num = count;<BR>&nbsp;&nbsp;&nbsp; public CalculateTask() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count++;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void <FONT color=#0000ff><STRONG>execute</STRONG></FONT>() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("[CalculateTask " + num + "] start...");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(3000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("[CalculateTask " + num + "] done.");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>// TimerTask.java<BR>package com.crackj2ee.thread;<BR>public class TimerTask implements Task {<BR>&nbsp;&nbsp;&nbsp; private static int count = 0;<BR>&nbsp;&nbsp;&nbsp; private int num = count;<BR>&nbsp;&nbsp;&nbsp; public TimerTask() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count++;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void <STRONG><FONT color=#0000ff>execute</FONT></STRONG>() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("[TimerTask " + num + "] start...");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(2000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("[TimerTask " + num + "] done.");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>以上任务均简单的sleep若干秒。</P>
<P>TaskQueue实现了一个队列，客户端可以将请求放入队列，服务器线程可以从队列中取出任务：</P>
<P>package com.crackj2ee.thread;<BR>import java.util.*;<BR>public class TaskQueue {<BR>&nbsp;&nbsp;&nbsp; private List queue = new LinkedList();<BR>&nbsp;&nbsp;&nbsp; public synchronized Task <STRONG><FONT color=#ff0000>getTask</FONT></STRONG>() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(queue.size()==0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.wait();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Task)queue.remove(0);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public synchronized void <FONT color=#ff0000><STRONG>putTask</STRONG></FONT>(Task task) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queue.add(task);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.notifyAll();<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>终于到了真正的WorkerThread，这是真正执行任务的服务器线程：</P>
<P>package com.crackj2ee.thread;<BR>public class WorkerThread extends Thread {<BR>&nbsp;&nbsp;&nbsp; private static int count = 0;<BR>&nbsp;&nbsp;&nbsp; private boolean busy = false;<BR>&nbsp;&nbsp;&nbsp; private boolean stop = false;<BR>&nbsp;&nbsp;&nbsp; private TaskQueue queue;<BR>&nbsp;&nbsp;&nbsp; public WorkerThread(ThreadGroup group, TaskQueue queue) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(group, "worker-" + count);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count++;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.queue = queue;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void shutdown() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stop = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.interrupt();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.join();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public boolean isIdle() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return !busy;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(getName() + " start.");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(!stop) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Task task = <FONT color=#ff0000><STRONG>queue.getTask();</STRONG></FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(task!=null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; busy = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; task.execute();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; busy = false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(getName() + " end.");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>前面已经讲过，queue.getTask()是一个阻塞方法，服务器线程可能在此wait()一段时间。此外，WorkerThread还有一个shutdown方法，用于安全结束线程。</P>
<P>最后是ThreadPool，负责管理所有的服务器线程，还可以动态增加和减少线程数：</P>
<P>package com.crackj2ee.thread;<BR>import java.util.*;<BR>public class ThreadPool extends ThreadGroup {<BR>&nbsp;&nbsp;&nbsp; private List threads = new LinkedList();<BR>&nbsp;&nbsp;&nbsp; private TaskQueue queue;<BR>&nbsp;&nbsp;&nbsp; public ThreadPool(TaskQueue queue) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super("Thread-Pool");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.queue = queue;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public synchronized void addWorkerThread() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread t = new WorkerThread(this, queue);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads.add(t);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.start();<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public synchronized void removeWorkerThread() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(threads.size()&gt;0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WorkerThread t = (WorkerThread)threads.remove(0);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.shutdown();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public synchronized void <FONT color=#ff0000><STRONG>currentStatus</STRONG></FONT>() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-----------------------------------------------");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Thread count = " + threads.size());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator it = threads.iterator();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(it.hasNext()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WorkerThread t = (WorkerThread)it.next();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(t.getName() + ": " + (t.isIdle() ? "idle" : "busy"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-----------------------------------------------");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>currentStatus()方法是为了方便调试，打印出所有线程的当前状态。</P>
<P>最后，Main负责完成main()方法：</P>
<P>package com.crackj2ee.thread;<BR>public class Main {<BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TaskQueue queue = new TaskQueue();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadPool pool = new ThreadPool(queue);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;10; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queue.putTask(new CalculateTask());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queue.putTask(new TimerTask());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doSleep(8000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.currentStatus();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.addWorkerThread();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doSleep(5000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.currentStatus();<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; private static void doSleep(long ms) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(ms);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>main()一开始放入了20个Task，然后动态添加了一些服务线程，并定期打印线程状态，运行结果如下：</P>
<P>worker-0 start.<BR>[CalculateTask 0] start...<BR>worker-1 start.<BR>[TimerTask 0] start...<BR>[TimerTask 0] done.<BR>[CalculateTask 1] start...<BR>[CalculateTask 0] done.<BR>[TimerTask 1] start...<BR>[CalculateTask 1] done.<BR>[CalculateTask 2] start...<BR>[TimerTask 1] done.<BR>[TimerTask 2] start...<BR>[TimerTask 2] done.<BR>[CalculateTask 3] start...<BR>-----------------------------------------------<BR>Thread count = 2<BR>worker-0: busy<BR>worker-1: busy<BR>-----------------------------------------------<BR>[CalculateTask 2] done.<BR>[TimerTask 3] start...<BR>worker-2 start.<BR>[CalculateTask 4] start...<BR>worker-3 start.<BR>[TimerTask 4] start...<BR>worker-4 start.<BR>[CalculateTask 5] start...<BR>worker-5 start.<BR>[TimerTask 5] start...<BR>worker-6 start.<BR>[CalculateTask 6] start...<BR>[CalculateTask 3] done.<BR>[TimerTask 6] start...<BR>[TimerTask 3] done.<BR>[CalculateTask 7] start...<BR>[TimerTask 4] done.<BR>[TimerTask 7] start...<BR>[TimerTask 5] done.<BR>[CalculateTask 8] start...<BR>[CalculateTask 4] done.<BR>[TimerTask 8] start...<BR>[CalculateTask 5] done.<BR>[CalculateTask 9] start...<BR>[CalculateTask 6] done.<BR>[TimerTask 9] start...<BR>[TimerTask 6] done.<BR>[TimerTask 7] done.<BR>-----------------------------------------------<BR>Thread count = 7<BR><STRONG><FONT color=#008080>worker-0: idle<BR></FONT></STRONG>worker-1: busy<BR>worker-2: busy<BR><STRONG><FONT color=#008080>worker-3: idle</FONT></STRONG><BR>worker-4: busy<BR>worker-5: busy<BR>worker-6: busy<BR>-----------------------------------------------<BR>[CalculateTask 7] done.<BR>[CalculateTask 8] done.<BR>[TimerTask 8] done.<BR>[TimerTask 9] done.<BR>[CalculateTask 9] done.</P>
<P><STRONG>仔细观察</STRONG>：一开始只有两个服务器线程，因此线程状态都是忙，后来线程数增多，6个线程中的两个状态变成idle，说明处于wait()状态。</P>
<P><FONT color=#ff0000><STRONG>思考</STRONG></FONT>：本例的线程调度算法其实根本没有，因为这个应用是围绕TaskQueue设计的，不是以Thread Pool为中心设计的。因此，Task调度取决于TaskQueue的getTask()方法，你可以改进这个方法，例如使用优先队列，使优先级高的任务先被执行。</P>
<P>如果所有的服务器线程都处于busy状态，则说明任务繁忙，TaskQueue的队列越来越长，最终会导致服务器内存耗尽。因此，可以限制 TaskQueue的等待任务数，超过最大长度就拒绝处理。许多Web服务器在用户请求繁忙时就会拒绝用户：HTTP 503 SERVICE UNAVAILABLE<BR></P>
<P>多线程读写同一个对象的数据是很普遍的，通常，要避免读写冲突，必须保证任何时候仅有一个线程在写入，有线程正在读取的时候，写入操作就必须等待。简单说，就是要避免“写-写”冲突和“读-写”冲突。但是同时读是允许的，因为“读-读”不冲突，而且很安全。</P>
<P>要实现以上的ReadWriteLock，简单的使用synchronized就不行，我们必须自己设计一个ReadWriteLock类，在读之前，必须先获得“读锁”，写之前，必须先获得“写锁”。举例说明：</P>
<P>DataHandler对象保存了一个可读写的char[]数组：</P>
<P>package com.crackj2ee.thread;<BR><BR>public class DataHandler {<BR>&nbsp;&nbsp;&nbsp; // store data:<BR>&nbsp;&nbsp;&nbsp; private char[] buffer = "AAAAAAAAAA".toCharArray();<BR><BR>&nbsp;&nbsp;&nbsp; private char[] doRead() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] ret = new char[buffer.length];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;buffer.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret[i] = buffer[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; private void doWrite(char[] data) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(data!=null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer = new char[data.length];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;buffer.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer[i] = data[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(10);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; private void sleep(int ms) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(ms);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>doRead()和doWrite()方法是非线程安全的读写方法。为了演示，加入了sleep()，并设置读的速度大约是写的3倍，这符合通常的情况。</P>
<P>为了让多线程能安全读写，我们设计了一个ReadWriteLock：</P>
<P>package com.crackj2ee.thread;<BR>public class ReadWriteLock {<BR>&nbsp;&nbsp;&nbsp; private int readingThreads = 0;<BR>&nbsp;&nbsp;&nbsp; private int writingThreads = 0;<BR>&nbsp;&nbsp;&nbsp; private int waitingThreads = 0; // waiting for write<BR>&nbsp;&nbsp;&nbsp; private boolean preferWrite = true;<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void readLock() throws InterruptedException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(writingThreads&gt;0 || (preferWrite &amp;&amp; waitingThreads&gt;0))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.wait();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; readingThreads++;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void readUnlock() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; readingThreads--;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; preferWrite = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notifyAll();<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void writeLock() throws InterruptedException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; waitingThreads++;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(readingThreads&gt;0 || writingThreads&gt;0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.wait();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; waitingThreads--;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writingThreads++;<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public synchronized void writeUnlock() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writingThreads--;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; preferWrite = false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notifyAll();<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>readLock()用于获得读锁，readUnlock()释放读锁，writeLock()和writeUnlock()一样。由于锁用完必须释放，因此，必须保证lock和unlock匹配。我们修改DataHandler，加入ReadWriteLock：</P>
<P>package com.crackj2ee.thread;<BR>public class DataHandler {<BR>&nbsp;&nbsp;&nbsp; // store data:<BR>&nbsp;&nbsp;&nbsp; private char[] buffer = "AAAAAAAAAA".toCharArray();<BR>&nbsp;&nbsp;&nbsp; // lock:<BR>&nbsp;&nbsp;&nbsp; private ReadWriteLock lock = new ReadWriteLock();<BR><BR>&nbsp;&nbsp;&nbsp; public char[] read(String name) throws InterruptedException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name + " waiting for read...");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG><FONT color=#ff0000>lock.readLock();</FONT></STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] data = doRead();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name + " reads data: " + new String(data));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return data;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG><FONT color=#ff0000>lock.readUnlock();<BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; public void write(String name, char[] data) throws InterruptedException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name + " waiting for write...");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG><FONT color=#ff0000>lock.writeLock();<BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name + " wrote data: " + new String(data));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doWrite(data);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG><FONT color=#ff0000>lock.writeUnlock();<BR></FONT></STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; private char[] doRead() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] ret = new char[buffer.length];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;buffer.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret[i] = buffer[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; private void doWrite(char[] data) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(data!=null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer = new char[data.length];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;buffer.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer[i] = data[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(10);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; private void sleep(int ms) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(ms);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>public方法read()和write()完全封装了底层的ReadWriteLock，因此，多线程可以安全地调用这两个方法：</P>
<P>// ReadingThread不断读取数据：<BR>package com.crackj2ee.thread;<BR>public class ReadingThread extends Thread {<BR>&nbsp;&nbsp;&nbsp; private DataHandler handler;<BR>&nbsp;&nbsp;&nbsp; public ReadingThread(DataHandler handler) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.handler = handler;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;;) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] data = handler.read(getName());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep((long)(Math.random()*1000+100));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>// WritingThread不断写入数据，每次写入的都是10个相同的字符：<BR>package com.crackj2ee.thread;<BR>public class WritingThread extends Thread {<BR>&nbsp;&nbsp;&nbsp; private DataHandler handler;<BR>&nbsp;&nbsp;&nbsp; public WritingThread(DataHandler handler) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.handler = handler;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] data = new char[10];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;;) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill(data);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handler.write(getName(), data);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep((long)(Math.random()*1000+100));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; //&nbsp;产生一个A-Z随机字符，填入char[10]:<BR>&nbsp;&nbsp;&nbsp; private void fill(char[] data) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char c = (char)(Math.random()*26+'A');<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;data.length; i++)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data[i] = c;<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>最后Main负责启动这些线程：</P>
<P>package com.crackj2ee.thread;<BR>public class Main {<BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataHandler handler = new DataHandler();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread[] ts = new Thread[] {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ReadingThread(handler),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ReadingThread(handler),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ReadingThread(handler),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ReadingThread(handler),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new ReadingThread(handler),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new WritingThread(handler),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new WritingThread(handler)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;ts.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ts[i].start();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>我们启动了5个读线程和2个写线程，运行结果如下：</P>
<P><FONT face="Courier New">Thread-0 waiting for read...<BR>Thread-1 waiting for read...<BR>Thread-2 waiting for read...<BR>Thread-3 waiting for read...<BR>Thread-4 waiting for read...<BR>Thread-5 waiting for write...<BR>Thread-6 waiting for write...<BR>Thread-4 reads data: AAAAAAAAAA<BR>Thread-3 reads data: AAAAAAAAAA<BR>Thread-2 reads data: AAAAAAAAAA<BR>Thread-1 reads data: AAAAAAAAAA<BR>Thread-0 reads data: AAAAAAAAAA<BR>Thread-5 wrote data: EEEEEEEEEE<BR>Thread-6 wrote data: MMMMMMMMMM<BR>Thread-1 waiting for read...<BR>Thread-4 waiting for read...<BR>Thread-1 reads data: MMMMMMMMMM<BR>Thread-4 reads data: MMMMMMMMMM<BR>Thread-2 waiting for read...<BR>Thread-2 reads data: MMMMMMMMMM<BR>Thread-0 waiting for read...<BR>Thread-0 reads data: MMMMMMMMMM<BR>Thread-4 waiting for read...<BR>Thread-4 reads data: MMMMMMMMMM<BR>Thread-2 waiting for read...<BR>Thread-5 waiting for write...<BR>Thread-2 reads data: MMMMMMMMMM<BR>Thread-5 wrote data: GGGGGGGGGG<BR>Thread-6 waiting for write...<BR>Thread-6 wrote data: AAAAAAAAAA<BR>Thread-3 waiting for read...<BR>Thread-3 reads data: AAAAAAAAAA<BR>......</FONT></P>
<P>可以看到，每次读/写都是完整的原子操作，因为我们每次写入的都是10个相同字符。并且，每次读出的都是最近一次写入的内容。</P>
<P>如果去掉ReadWriteLock：</P>
<P>package com.crackj2ee.thread;<BR>public class DataHandler {<BR><BR>&nbsp;&nbsp;&nbsp; // store data:<BR>&nbsp;&nbsp;&nbsp; private char[] buffer = "AAAAAAAAAA".toCharArray();<BR><BR>&nbsp;&nbsp;&nbsp; public char[] read(String name) throws InterruptedException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] data = doRead();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name + " reads data: " + new String(data));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return data;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void write(String name, char[] data) throws InterruptedException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name + " wrote data: " + new String(data));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doWrite(data);<BR>&nbsp;&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp;&nbsp; private char[] doRead() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char[] ret = new char[10];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;10; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret[i] = buffer[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; private void doWrite(char[] data) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;10; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buffer[i] = data[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(10);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; private void sleep(int ms) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(ms);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(InterruptedException ie) {}<BR>&nbsp;&nbsp;&nbsp; }<BR>}</P>
<P>运行结果如下：</P>
<P><FONT face="Courier New">Thread-5 wrote data: AAAAAAAAAA<BR>Thread-6 wrote data: MMMMMMMMMM<BR>Thread-0 reads data: AAAAAAAAAA<BR>Thread-1 reads data: AAAAAAAAAA<BR>Thread-2 reads data: AAAAAAAAAA<BR>Thread-3 reads data: AAAAAAAAAA<BR>Thread-4 reads data: AAAAAAAAAA<BR>Thread-2 reads data: MAAAAAAAAA<BR>Thread-3 reads data: MAAAAAAAAA<BR>Thread-5 wrote data: CCCCCCCCCC<BR>Thread-1 reads data: MAAAAAAAAA<BR>Thread-0 reads data: MAAAAAAAAA<BR>Thread-4 reads data: MAAAAAAAAA<BR></FONT><STRONG><FONT face="Courier New" color=#ff0000>Thread-6 wrote data: EEEEEEEEEE<BR>Thread-3 reads data: EEEEECCCCC<BR>Thread-4 reads data: EEEEEEEEEC<BR>Thread-1 reads data: EEEEEEEEEE</FONT></STRONG></P>
<P>可以看到在Thread-6写入EEEEEEEEEE的过程中，3个线程读取的内容是不同的。</P>
<P><FONT color=#ff0000><STRONG>思考</STRONG></FONT></P>
<P>java的synchronized提供了最底层的物理锁，要在synchronized的基础上，实现自己的逻辑锁，就必须仔细设计ReadWriteLock。</P>
<P>Q: lock.readLock()为什么不放入try{ }&nbsp;内？<BR>A: 因为readLock()会抛出InterruptedException，导致readingThreads++不执行，而readUnlock()在 finally{ } 中，导致readingThreads--执行，从而使readingThread状态出错。writeLock()也是类似的。</P>
<P>Q: preferWrite有用吗？<BR>A: 如果去掉preferWrite，线程安全不受影响。但是，如果读取线程很多，上一个线程还没有读取完，下一个线程又开始读了，就导致写入线程长时间无法获得writeLock；如果写入线程等待的很多，一个接一个写，也会导致读取线程长时间无法获得readLock。preferWrite的作用是让读 /写交替执行，避免由于读线程繁忙导致写无法进行和由于写线程繁忙导致读无法进行。</P>
<P>Q: notifyAll()换成notify()行不行？<BR>A: 不可以。由于preferWrite的存在，如果一个线程刚读取完毕，此时preferWrite=true，再notify()，若恰好唤醒的是一个读线程，则while(writingThreads&gt;0 || (preferWrite &amp;&amp; waitingThreads&gt;0))可能为true导致该读线程继续等待，而等待写入的线程也处于wait()中，结果所有线程都处于wait ()状态，谁也无法唤醒谁。因此，notifyAll()比notify()要来得安全。程序验证notify()带来的死锁：</P>
<P><FONT face="Courier New">Thread-0 waiting for read...<BR>Thread-1 waiting for read...<BR>Thread-2 waiting for read...<BR>Thread-3 waiting for read...<BR>Thread-4 waiting for read...<BR>Thread-5 waiting for write...<BR>Thread-6 waiting for write...<BR>Thread-0 reads data: AAAAAAAAAA<BR>Thread-4 reads data: AAAAAAAAAA<BR>Thread-3 reads data: AAAAAAAAAA<BR>Thread-2 reads data: AAAAAAAAAA<BR>Thread-1 reads data: AAAAAAAAAA<BR>Thread-5 wrote data: CCCCCCCCCC<BR>Thread-2 waiting for read...<BR>Thread-1 waiting for read...<BR>Thread-3 waiting for read...<BR>Thread-0 waiting for read...<BR>Thread-4 waiting for read...<BR>Thread-6 wrote data: LLLLLLLLLL<BR>Thread-5 waiting for write...<BR>Thread-6 waiting for write...<BR>Thread-2 reads data: LLLLLLLLLL<BR>Thread-2 waiting for read...<BR>（运行到此不动了）</FONT></P>
<P>注意到这种死锁是由于所有线程都在等待别的线程唤醒自己，结果都无法醒过来。这和两个线程希望获得对方已有的锁造成死锁不同。因此多线程设计的难度远远高于单线程应用。</P><img src ="http://www.blogjava.net/iNeo/aggbug/23754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iNeo/" target="_blank">只牵这只狗</a> 2005-12-14 08:54 <a href="http://www.blogjava.net/iNeo/articles/23754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在不容许使用第三个变量下，实现一个函数，把两个整型参数的值对调!</title><link>http://www.blogjava.net/iNeo/articles/21551.html</link><dc:creator>只牵这只狗</dc:creator><author>只牵这只狗</author><pubDate>Sun, 27 Nov 2005 04:43:00 GMT</pubDate><guid>http://www.blogjava.net/iNeo/articles/21551.html</guid><wfw:comment>http://www.blogjava.net/iNeo/comments/21551.html</wfw:comment><comments>http://www.blogjava.net/iNeo/articles/21551.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/iNeo/comments/commentRss/21551.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/iNeo/services/trackbacks/21551.html</trackback:ping><description><![CDATA[解答一:<BR>swap(int a, int b)<BR>{<BR>&nbsp;&nbsp;a -= b;&nbsp;&nbsp;// a(new) = a-b<BR>&nbsp;&nbsp;b += a;&nbsp;&nbsp;// b(new) = b+a(new) = b+(a-b)=a<BR>&nbsp;&nbsp;a = b-a;&nbsp;&nbsp;// a(result) = b-a(new)= a-(a-b)=b<BR>} <BR><BR>解答二:<BR>
<TABLE class=tablenormal>
<TBODY>
<TR>
<TD vAlign=top height=150><!--帖子评价 --><!--是否正解 --><!--内容 -->b=(a=(b=(a^b))^a)^b<BR><BR>解答三:<BR>a = a^(a^b);<BR>b = b^(a^b); <!-- the post be hidden --></TD></TR></TBODY></TABLE><BR><!-- the post be hidden --><img src ="http://www.blogjava.net/iNeo/aggbug/21551.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/iNeo/" target="_blank">只牵这只狗</a> 2005-11-27 12:43 <a href="http://www.blogjava.net/iNeo/articles/21551.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>