??xml version="1.0" encoding="utf-8" standalone="yes"?> 2005-12-18 17:35 作? canonical【评?3?/FONT>【阅?97?/P>
良好的系l架构应该体现出应用本n的结构要? 所谓各个ؓ自己, 架构为大? 只要各个局部符合规? 应该由架构负责在合适的时刻按照合适的方式把它们组装在一? 一个良好的架构? 应该很少出现l构性的if语句, 不需要应用代码自己通过动态判断来定义某个Ҏ(gu)的触发时? 架构是一U规? 当然也就是一U局? 架构的可退化性是非常重要? 否则一旦出现抽象泄? 需要超出原有架构设计做出编码补充的时? 往往无法代码自然的融入原有的框架结? 则整个框架出现大面积的失效情? 而有的时候更p糕的情冉|一些关键性的资源处在原有技术架构的U有控制之中, 我们Z克服架构限制不得不采用各Utrick来hack原有框架, 造成错误的篏加和传播, 而补丁的补丁是最隄护的.
架构问题q不是一成不变的. 在一些情形下无关紧要的问题在另一U情形下可能会成为灾难性的架构问题. 例如在多层B/S架构? 如果现在要求为每一个表增加一个对应的历史? q对其进行查看和l护操作. Z最大限度的重用代码, q要求我们的多层l构中的每一层都能够参数? q样我们才能用同L(fng)代码处理不同的数据表. 如果我们的money很, 弟够多, 有够的人月怸? 那么我们完全可以把业务表和历史表分开处理, 但如果反?我们׃遇到一个典型的架构问题.
架构师未必有自己的框? 因ؓ设计不等价于创? 架构师只要知道如何把pȝ中的各种元素按照可行的方式组装在一起就可以? 但是一个架构设计是非常依赖于我们所能采用的技术手D늚, 当现有各U可用的技术元素都无法满我们的需求的时? 某些架构师可能会选择创造一U技术元? 当然, 创造是艰难? 它所要求的甚x不同的技? Sun的Green目创造了java语言, 从而开启了一个伟大的时代, q绝对不会是大多数架构设计师的选择(有趣的是,Green目本np|?. EJB现在q有多少人在真正使用, x当年多少架构师在吹嘘q些东西. 他们对于技术的把握真的那么幼E吗? 架构设计q不是凭I出现的, 当时可选的东西是如此, 而spring和hibernateq些都不属于架构设计本n的内?它们是一U创?
架构师未必是团队的领D? 实,他的工作cM于编? 负责执行的一般是导演. 事实?一个徏{设计师是极直接领g个工E队?架构师也未必比高U程序员要高? 他们负责的是不同的内? 至于产品?商标及商标的相关元素"?技术市场架?{也不属于架构师的工作范? 他不能去抢品经理的饭碗. 当然,在国内的现实情况? 很多所谓的架构师所做的最重要的工作可能是公关工作, 向客L(fng)出所谓的理念, 与实际开发是不搭嘎的.
理论上说, 架构师可以不是编E的? 也可以不军_一些具体数据结构的选择, 但他不能不了解各U技术抉择潜在的影响. q就如同一个徏{设计师可以不精通工E力?但是他不能愚蠢到藐视重力, 设计出倒三角式的大? 与徏{不同的? 在Y件中我们所面(f)的不是一U?凝固的艺?, 我们无法以完全静态的方式理解代码,而必d头脑中把它们q行h. 架构师应该写下一些实际的代码, 以检验各个接口的可配合性ƈ获得对于代码l构的直接感? 实际? 按照现在软g业的成熟? 一般我们无法实现徏{中建筑设计师与土木工程师的分工, 很多时候Y件架构师都需要直接面对实现的l节. 如果l内~Z非常强?zhn)的coder, 有编E能力的架构师亲自操刀实现关键性代码的时候也是很多的.
架构师必Ll验, 但他所依赖的不能只是经? 只要一架构师的年U? ׃知道以他们在q个世界上的存在旉, q不以使得他们l历各种技术细? 架构设计更多的是依赖我们对于pȝl构原理的理? 而经验可以让我们规避那些原理失效的地?例如pȝUbug). 君子非异能也, 善假于物? 很多时?我们更应该从有经验的朋友或者技术支持那里搜集技术细? 以确保它们能够满x们在架构上的原理性需? Know Why而不仅仅是Know How是非帔R要的. 一个农民发明家也许可以得到某个巧妙的机械设? 但是没有pȝ的掌握工E力? 他们是无法去开发精密的导弹控制pȝ?当然, 软g开发还处在非常原始的阶D? 掌握一些设计原理和设计模式多半也不q是五十步笑百步而已, l验的地位是无可替代?
架构师不是预a? 在多变的业务环境? 架构师的目标不应该是预测到所有的变化可能, q把它们表达到系l架构中. q个世界上不乏一些耗资数十?设计三四q?但最l每个谈到它的h都要说一句shit的品开发项? 架构设计所能做到的最好的E度是自然的标注出系l的l构边界,成功的delay各种技术抉?
架构师不是超? 他所考虑的东西也许要q一? 所需要^衡的利益也许要多一? 但是单独一个h是无法对整个产品或者项目的成|负责? 如果ThoughtWorks的Martin Follower来处理国内的某些目, 我估计他会死得很隄.架构师也是h, 也会犯错?甚至是很低的错? 而每个h都会有一些独特的x. l历的多? 你就会回归到l极的认? 一切都只是云, 只有money才是道?
其中"StrCommand"代表以下命o之一(使用Windows中的q行不要加引?Q?
"rundll32 shell32,Control_RunDLL" - q行控制面板
"rundll32 shell32,OpenAs_RunDLL" - 打开"打开方式"H口
"rundll32 shell32,ShellAboutA Info-Box" - 打开"关于"H口
"rundll32 shell32,Control_RunDLL desk.cpl" - 打开"昄属?H口
"rundll32 user,cascadechildwindows" - 层叠全部H口
"rundll32 user,tilechildwindows" - 最化所有的子窗?
"rundll32 user,repaintscreen" - h桌面
"rundll32 shell,shellexecute Explorer" - 重新q行W(xu)indows Explorer
"rundll32 keyboard,disable" - 锁写键盘
"rundll32 mouse,disable" - 让鼠标失?
"rundll32 user,swapmousebutton" - 交换鼠标按钮
"rundll32 user,setcursorpos" - 讄鼠标位置?0,0)
"rundll32 user,wnetconnectdialog" - 打开"映射|络驱动?H口
"rundll32 user,wnetdisconnectdialog" - 打开"断开|络驱动?H口
"rundll32 user,disableoemlayer" - 昄BSODH口, (BSOD) = Blue Screen Of
Death, 卌?
"rundll32 diskcopy,DiskCopyRunDll" - 打开盘复制H口
"rundll32 rnaui.dll,RnaWizard" - q行"Internetq接向导",
如果加上参数"/1"则ؓsilent模式
"rundll32 shell32,SHFormatDrive" - 打开"格式化磁?A)"H口
"rundll32 shell32,SHExitWindowsEx -1" - 冷启动Windows Explorer
"rundll32 shell32,SHExitWindowsEx 1" - x
"rundll32 shell32,SHExitWindowsEx 0" - 退当前用户
"rundll32 shell32,SHExitWindowsEx 2" Windows9x 快速重?
"rundll32 krnl386.exe,exitkernel" - 退出Windows 9x(无确?
"rundll rnaui.dll,RnaDial "MyConnect" - q行"|络q接"对话?
"rundll32 msprint2.dll,RUNDLL_PrintTestPage" - 选择打印机和打印试?
"rundll32 user,setcaretblinktime" - 讄光标闪烁速度
"rundll32 user, setdoubleclicktime" - 试鼠标双击速度
"rundll32 sysdm.cpl,InstallDevice_Rundll" - 搜烦非PnP讑֤
控制面板中的各项功能
winexec('rundll32.exe shell32.dll, Control_RunDLL', 9);
{辅助选项 属?键盘}
winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 1', 9);
{辅助选项 属?声音}
winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 2', 9);
{辅助选项 属?昄}
winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 3', 9);
{辅助选项 属?鼠标}
winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 4', 9);
{辅助选项 属?常规}
winexec('rundll32.exe shell32.dll, Control_RunDLL access.cpl, 5', 9);
{d/删除E序 属?安装/卸蝲}
winexec('rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 1', 9);
{d/删除E序 属?Windows安装E序}
winexec('rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 2', 9);
{d/删除E序 属?启动盘}
winexec('rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 3', 9);
{昄 属?背景}
winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 0', 9);
{昄 属?屏幕保护E序}
winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 1', 9);
{昄 属?外观}
winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 2', 9);
{昄 属?讄}
winexec('rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 3', 9);
{Internet 属?常规}
winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 0',
9);
{Internet 属?安全}
winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 1',
9);
{Internet 属?内容}
winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 2',
9);
{Internet 属?q接}
winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 3',
9);
{Internet 属?E序}
winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 4',
9);
{Internet 属?高}
winexec('rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 5',
9);
{区域讄 属?区域讄}
winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 0', 9);
{区域讄 属?数字}
winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 1', 9);
{区域讄 属?货币}
winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 2', 9);
{区域讄 属?旉}
winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 3', 9);
{区域讄 属?日期}
winexec('rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 4', 9);
winexec('rundll32.exe shell32.dll, Control_RunDLL Joy.cpl, 0', 9);
winexec('rundll32.exe shell32.dll, Control_RunDLL Joy.cpl, 1', 9);
{鼠标 属性}
winexec('rundll32.exe shell32.dll, Control_RunDLL Main.cpl', 9);
{多媒?属?音频}
winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 0', 9);
{多媒?属?视频}
winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 1', 9);
{多媒?属?MIDI}
winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 2', 9);
{多媒?属?CD音乐}
winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 3', 9);
{多媒?属?讑֤}
winexec('rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 4', 9);
{调制解调?属性}
winexec('rundll32.exe shell32.dll, Control_RunDLL Modem.cpl', 9);
winexec('rundll32.exe shell32.dll, Control_RunDLL Netcpl.cpl', 9);
{密码 属性}
winexec('rundll32.exe shell32.dll, Control_RunDLL Password.cpl', 9);
{扫描仪与数字相机 属性}
winexec('rundll32.exe shell32.dll, Control_RunDLL Sticpl.cpl', 9);
{pȝ 属?常规}
winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 0', 9);
{pȝ 属?讑֤理器}
winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 1', 9);
{pȝ 属?g配置文g}
winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 2', 9);
{pȝ 属?性能}
winexec('rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 3', 9);
{日期/旉 属性}
winexec('rundll32.exe shell32.dll, Control_RunDLL timedate.cpl', 9);
{甉|理 属性}
winexec('rundll32.exe shell32.dll, Control_RunDLL Powercfg.cpl', 9);
winexec('rundll32.exe shell32.dll, Control_RunDLL Telephon.cpl', 9);
关于调用后的判断处理Q?
先声明一个cardinalcd的变量RtnCardinal获取q回D行判断如Q?
RtnCardinal := winexec('rundll32.exe shell32.dll, Control_RunDLL
Telephon.cpl', 9);
q回?可能原因
0 E序出内存
ERROR_BAD_FORMAT E序Z个非法的Win32.EXEE序
ERROR_FILE_NOT_FOUND 指定文g没找到
ERROR_PATH_NOT_FOUND 指定路径没找?
使用Ҏ(gu)Q?
点击“开始-E式QMsQDos方式”,q入Dos视窗Q然後键?rundll32.exe
user.exe,restartwindows"Q再按下回R键,q时你将看到Q机器被重启了!怎么P是不是很有趣Q?
当然QRundll的功能绝不仅仅是重启你的机器。其实,Rundll者,思义Q执行Dll也,它的功能是以命令列的方式呼叫Windows的动态链l库QRundll32.exe与Rundll.exe的区别就在於前者是呼叫32位的铄库,而後者是q用?6位的铄库,它们的命令格式是Q?
RUNDLL.EXE Q,
q里要注意三点:1.Dll案名中不能含有I格Q比如该案位於c:Program
Files目录Q你要把q个路径Ҏ(gu)c:Progra?Q?.Dll档案名与Dll入口炚w的逗号不能,否则E式出错ƈ且不会给ZQ何资讯!3.q是最重要的一点:Rundll不能用来呼叫含返回值参数的DllQ例如Win32API中的GetUserName(),GetTextFace(){。在Visual
Basic中,提供了一条执行外部程式的指oShell,格式为:
Shell “命令列?
如果能配合Rundll32.exe用好Shell指oQ会使?zhn)的VBE式拥有用其他方法难以甚x法实现的效果Q仍以重启ؓ例,传统的方法需要你在VB工程中先建立一个模l,然後写入WinAPI的声明,最後才能在E式中呼叫。而现在只需一?
Shell “rundll32.exe
user.exe,restartwindows”就搞定了!是不是方便多了?
实际上,Rundll32.exe在呼叫各UWindows控制面板和系l选项斚w有著独特的优ѝ下面,我就本人在因特|上攉的有关Rundll的指令列丑֦下(很有用的Q能省去你很多呼叫Windows
API的时_Q)Q供大家在程式设计中引用Q?
命o? rundll32.exe shell32.dll,Control_RunDLL
功能: 昄控制面板
命o? rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1
功能: 昄“控刉板-辅助选项Q键盘”选项视窗
命o? rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2
功能: 昄“控刉板-辅助选项Q声音”选项视窗
命o? rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3
功能: 昄“控刉板-辅助选项Q显C”选项视窗
命o? rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4
功能: 昄“控刉板-辅助选项Q滑鼠”选项视窗
命o? rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5
功能: 昄“控刉板-辅助选项Q传l”选项视窗
命o? rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1
功能: 执行“控刉板-d新硬体”向对{?
命o? rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter
功能: 执行“控刉板-d新印表机”向对{?
命o? rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,1
功能: 昄 “控刉板-d/删除E式Q安?卸蝲?面板?
命o? rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,2
功能: 昄 “控刉板-d/删除E式Q安装Windows?面板?
命o? rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,3
功能: 昄 “控刉板-d/删除E式Q启动盘?面板?
命o? rundll32.exe syncui.dll,Briefcase_Create
功能: 在桌面上建立一个新的“我的公文包”?
命o? rundll32.exe diskcopy.dll,DiskCopyRunDll
功能: 昄复制软碟视窗
命o? rundll32.exe apwiz.cpl,NewLinkHere Q?
功能:
昄“徏立快h式”的对话框,所建立的快h式的位置由%1参数军_?
命o? rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,0
功能: 昄“日期与旉”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,1
功能: 昄“时区”选项视窗?
命o? rundll32.exe rnaui.dll,RnaDial [某个拨号q接的名U]
功能:
昄某个拨号q接的拨可H。如果已l拨可接,则显C目前的q接状态的视窗?
命o? rundll32.exe rnaui.dll,RnaWizard
功能: 昄“新建拨可接”向导的视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0
功能: 昄“显C属性-背景”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1
功能: 昄“显C属性-萤屏保护”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2
功能: 昄“显C属性-外观”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3
功能: 昄昄“显C属性-属性”选项视窗?
命o? rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL FontsFolder
功能: 昄Windows的“字体”案夹?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @3
功能: 同样是显CWindows的“字体”案夹?
命o? rundll32.exe shell32.dll,SHformatDrive
功能: 昄格式化Y对话框?
命o? rundll32.exe shell32.dll,Control_RunDLL joy.cpl,,0
功能: 昄“控刉板-游戏控制器-一般”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL joy.cpl,,1
功能: 昄“控刉板-游戏控制器-q阶”选项视窗?
命o? rundll32.exe mshtml.dll,PrintHTML (HTML文档)
功能: 列印HTML文?
命o? rundll32.exe shell32.dll,Control_RunDLL mlcfg32.cpl
功能: 昄Microsoft Exchange一般选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @0
功能: 昄“控刉板-滑鼠?选项 ?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @1
功能: 昄 “控刉板-键盘属性-速度”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @1,,1
功能: 昄 “控刉板-键盘属性-语言”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @2
功能: 昄Windows“印表机”档案夹?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @3
功能: 昄Windows“字体”案夹?
命o? rundll32.exe shell32.dll,Control_RunDLL main.cpl @4
功能: 昄“控刉板-输入法属性-输入法”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add
功能: 执行“添加新调制解调器”向对{?
命o? rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,0
功能: 昄“控刉板-多媒体属性-音频”属性页?
命o? rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1
功能: 昄“控刉板-多媒体属性-视频”属性页?
命o? rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,2
功能: 昄“控刉板-多媒体属性-MIDI”属性页?
命o? rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,3
功能: 昄“控刉板-多媒体属性-CD音乐”属性页?
命o? rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,4
功能: 昄“控刉板-多媒体属性-讑֤”属性页?
命o? rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl @1
功能: 昄“控刉板-声音”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl
功能: 昄“控刉板-|\”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL odbccp32.cpl
功能: 昄ODBC32资料理选项视窗?
命o? rundll32.exe shell32.dll,OpenAs_RunDLL
功能: 昄指定案(drive:pathfilename)的“打开方式”对话框?
命o? rundll32.exe shell32.dll,Control_RunDLL password.cpl
功能: 昄“控刉板-密码”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL powercfg.cpl
功能: 昄“控刉板-甉|理属性”选项视窗?
命o? rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL
PrintersFolder
功能: 昄Windows“印表机”案夹?同rundll32.exe
shell32.dll,Control_RunDLL main.cpl @2)
命o? rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,0
功能: 昄“控刉板-区域讄属性-区域讄”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,1
功能: 昄“控刉板-区域讄属性-数字”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,2
功能: 昄“控刉板-区域讄属性-货币”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,3
功能: 昄“控刉板-区域讄属性-旉”选项视窗?
命o? rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,4
功能: 昄“控刉板-区域讄属性-日期”选项视窗?
命o? rundll32.exe desk.cpl,InstallScreenSaver [萤屏保护档案名]
功能:
指定的萤屏保护案讄为Windows的屏保,q显C屏保护属性视H?
命o? rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,0
功能: 昄“控刉板-pȝ属性-传统”属性视H?
命o? rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,1
功能: 昄“控刉板-pȝ属性-讑֤理器”属性视H?
命o? rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,2
功能: 昄“控刉板-pȝ属性-体配置案”属性视H?
命o? rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,3
功能: 昄“控刉板-pȝ属性-性能”属性视H?
命o? rundll32.exe user.exe,restartwindows
功能: 关闭所有程式ƈ重启机器?
命o? rundll32.exe user.exe,exitwindows
功能: 关闭所有程式ƈx?
命o? rundll32.exe shell32.dll,Control_RunDLL telephon.cpl
功能: 昄“拨号属性”选项视窗
命o? rundll32.exe shell32.dll,Control_RunDLL themes.cpl
功能: 昄“桌面主旨”选项面板
当然Q不止是VisualBasicQ象Delphi.VisualCQ+{其他程式设计语a也可以通过呼叫外部命o的方法来使用Rundll的这些功能,具体Ҏ(gu)q里׃再详l叙qC。灵zȝ使用Rundll,一定会使你的程式设计轻L松,辑ֈ事半功倍的效果
java语言已经内置了多U程支持Q所有实现Runnable接口的类都可被启动一个新U程Q新U程会执行该实例的run()Ҏ(gu)Q当run()Ҏ(gu)执行完毕后,U程q束了。一旦一个线E执行完毕,q个实例׃能再重新启动Q只能重新生成一个新实例Q再启动一个新U程?/P>
ThreadcL实现了Runnable接口的一个实例,它代表一个线E的实例Qƈ且,启动U程的唯一Ҏ(gu)是通过Threadcȝstart()实例Ҏ(gu)Q?/P>
Thread t = new Thread();
t.start();
start()Ҏ(gu)是一个nativeҎ(gu)Q它?yu)启动一个新U程Qƈ执行run()Ҏ(gu)。Threadc默认的run()Ҏ(gu)什么也不做退Z。注意:直接调用run()Ҏ(gu)q不会启动一个新U程Q它和调用一个普通的javaҎ(gu)没有什么区别?/P>
因此Q有两个Ҏ(gu)可以实现自己的线E:
Ҏ(gu)1Q自qcextend ThreadQƈ复写run()Ҏ(gu)Q就可以启动新线Eƈ执行自己定义的run()Ҏ(gu)。例如:
public class MyThread extends Thread {
public run() {
System.out.println("MyThread.run()");
}
}
在合适的地方启动U程Qnew MyThread().start();
Ҏ(gu)2Q如果自qcdlextends另一个类Q就无法直接extends ThreadQ此Ӟ必须实现一个Runnable接口Q?/P>
public class MyThread extends OtherClass implements Runnable {
public run() {
System.out.println("MyThread.run()");
}
}
Z启动MyThreadQ需要首先实例化一个ThreadQƈ传入自己的MyThread实例Q?/P>
MyThread myt = new MyThread();
Thread t = new Thread(myt);
t.start();
事实上,当传入一个Runnable target参数lThread后,Thread的run()Ҏ(gu)׃调用target.run()Q参考JDK源代码:
public void run() {
if (target != null) {
target.run();
}
}
U程q有一些Name, ThreadGroup, isDaemon{设|,׃和线E设计模式关联很,q里׃多说了?BR>
׃同一q程内的多个U程׃n内存I间Q在Java中,是׃n实例Q当多个U程试图同时修改某个实例的内Ҏ(gu)Q就会造成冲突Q因此,U程必须实现׃n互斥Q多线E同步?/P>
最单的同步是将一个方法标CؓsynchronizedQ对同一个实例来_M时刻只能有一个synchronizedҎ(gu)在执行。当一个方法正在执行某个synchronizedҎ(gu)Ӟ其他U程如果惌执行q个实例的Q意一个synchronizedҎ(gu)Q都必须{待当前执行 synchronizedҎ(gu)的线E退出此Ҏ(gu)后,才能依次执行?/P>
但是Q非synchronizedҎ(gu)不受影响Q不当前有没有执行synchronizedҎ(gu)Q非synchronizedҎ(gu)都可以被多个U程同时执行?/P>
此外Q必L意,只有同一实例的synchronizedҎ(gu)同一旉只能被一个线E执行,不同实例的synchronizedҎ(gu)是可以ƈ发的。例如,class A定义了synchronizedҎ(gu)sync()Q则不同实例a1.sync()和a2.sync()可以同时׃个线E来执行?BR>
多线E同步的实现最l依赖锁机制。我们可以想象某一׃n资源是一间屋子,每个人都是一个线E。当A希望q入戉KӞ他必获得门锁,一旦A获得门锁Q他q去后就立刻门锁上Q于是B,C,D...׃得不在门外等待,直到A释放锁出来后QB,C,D...中的某一人抢C该锁Q具体抢法依赖于 JVM的实玎ͼ可以先到先得Q也可以随机挑选)Q然后进屋又门锁上。这PM时刻最多有一人在屋内Q用共享资源)?/P>
Java语言规范内置了对多线E的支持。对于JavaE序来说Q每一个对象实例都有一把“锁”,一旦某个线E获得了该锁Q别的线E如果希望获得该锁,只能{待q个U程释放锁之后。获得锁的方法只有一个,是synchronized关键字。例如:
public class SharedResource {
private int count = 0;
public int getCount() { return count; }
public synchronized void setCount(int count) { this.count = count; }
}
同步Ҏ(gu)public synchronized void setCount(int count) { this.count = count; } 事实上相当于Q?/P>
public void setCount(int count) {
synchronized(this) { // 在此获得this?BR> this.count = count;
} // 在此释放this?BR>}
U色部分表示需要同步的代码D,该区域ؓ“危险区域”,如果两个以上的线E同时执行,会引发冲H,因此Q要更改SharedResource的内部状态,必须先获得SharedResource实例的锁?/P>
退出synchronized块时Q线E拥有的锁自动释放,于是Q别的线E又可以获取该锁了?/P>
Z提高性能Q不一定要锁定thisQ例如,SharedResource有两个独立变化的变量Q?/P>
public class SharedResouce {
private int a = 0;
private int b = 0;
public synchronized void setA(int a) { this.a = a; }
public synchronized void setB(int b) { this.b = b; }
}
若同步整个方法,则setA()的时候无法setB()QsetB()时无法setA()。ؓ了提高性能Q可以用不同对象的锁:
public class SharedResouce {
private int a = 0;
private int b = 0;
private Object sync_a = new Object();
private Object sync_b = new Object();
public void setA(int a) {
synchronized(sync_a) {
this.a = a;
}
}
public synchronized void setB(int b) {
synchronized(sync_b) {
this.b = b;
}
}
}
通常Q多U程之间需要协调工作。例如,览器的一个显C图片的U程displayThread惌执行昄囄的Q务,必须{待下蝲U程 downloadThread该囄下蝲完毕。如果图片还没有下蝲完,displayThread可以暂停Q当downloadThread完成了Q务后Q再通知displayThread“图片准备完毕,可以昄了”,q时QdisplayThreadl箋执行?/P>
以上逻辑单的说就是:如果条g不满I则等待。当条g满Ӟ{待该条件的U程被唤醒。在Java中,q个机制的实C赖于wait/notify。等待机制与锁机制是密切兌的。例如:
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
}
当线EA获得了obj锁后Q发现条件condition不满I无法l箋下一处理Q于是线EAwait()?/P>
在另一U程B中,如果B更改了某些条Ӟ使得U程A的condition条g满了,可以唤醒线EAQ?/P>
synchronized(obj) {
condition = true;
obj.notify();
}
需要注意的概念是:
# 调用obj的wait(), notify()Ҏ(gu)前,必须获得obj锁,也就是必d在synchronized(obj) {...} 代码D内?/P>
# 调用obj.wait()后,U程A释放了obj的锁Q否则线EB无法获得obj锁,也就无法在synchronized(obj) {...} 代码D内唤醒A?/P>
# 当obj.wait()Ҏ(gu)q回后,U程A需要再ơ获得obj锁,才能l箋执行?/P>
# 如果A1,A2,A3都在obj.wait()Q则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM军_Q?/P>
# obj.notifyAll()则能全部唤醒A1,A2,A3Q但是要l箋执行obj.wait()的下一条语句,必须获得obj锁,因此QA1,A2,A3只有一个有Z获得锁l执行,例如A1Q其余的需要等待A1释放obj锁之后才能l执行?/P>
# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此QA1,A2,A3虽被唤醒Q但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后QA1,A2,A3中的一个才有机会获得锁l箋执行?/P>
前面讲了wait/notify机制QThreadq有一个sleep()静态方法,它也能ɾU程暂停一D|间。sleep与wait的不同点是: sleepq不释放锁,q且sleep的暂停和wait暂停是不一L(fng)。obj.wait会ɾU程q入obj对象的等待集合中q等待唤醒?/P>
但是wait()和sleep()都可以通过interrupt()Ҏ(gu)打断U程的暂停状态,从而ɾU程立刻抛出InterruptedException?/P>
如果U程A希望立即l束U程BQ则可以对线EB对应的Thread实例调用interruptҎ(gu)。如果此ȝEB正在 wait/sleep/joinQ则U程B会立L出InterruptedExceptionQ在catch() {} 中直接return卛_安全地结束线E?/P>
需要注意的是,InterruptedException是线E自׃内部抛出的,q不是interrupt()Ҏ(gu)抛出的。对某一U程调用 interrupt()Ӟ如果该线E正在执行普通的代码Q那么该U程Ҏ(gu)׃会抛出InterruptedException。但是,一旦该U程q入?wait()/sleep()/join()后,׃立刻抛出InterruptedException?/P>
GuardedSuspention模式主要思想是:
当条件不满ӞU程{待Q直到条件满xQ等待该条g的线E被唤醒?/P>
我们设计一个客L(fng)U程和一个服务器U程Q客L(fng)U程不断发送请求给服务器线E,服务器线E不断处理请求。当h队列为空Ӟ服务器线E就必须{待Q直到客L(fng)发送了h?/P>
先定义一个请求队列:Queue
package com.crackj2ee.thread;
import java.util.*;
public class Queue {
private List queue = new LinkedList();
public synchronized Request getRequest() {
while(queue.size()==0) {
try {
this.wait();
}
catch(InterruptedException ie) {
return null;
}
}
return (Request)queue.remove(0);
}
public synchronized void putRequest(Request request) {
queue.add(request);
this.notifyAll();
}
}
蓝色部分是服务器线E的{待条gQ而客L(fng)U程在放入了一个request后,׃服务器线E等待条件满I于是唤醒服务器线E?/P>
客户端线E:ClientThread
package com.crackj2ee.thread;
public class ClientThread extends Thread {
private Queue queue;
private String clientName;
public ClientThread(Queue queue, String clientName) {
this.queue = queue;
this.clientName = clientName;
}
public String toString() {
return "[ClientThread-" + clientName + "]";
}
public void run() {
for(int i=0; i<100; i++) {
Request request = new Request("" + (long)(Math.random()*10000));
System.out.println(this + " send request: " + request);
queue.putRequest(request);
try {
Thread.sleep((long)(Math.random() * 10000 + 1000));
}
catch(InterruptedException ie) {
}
}
System.out.println(this + " shutdown.");
}
}
服务器线E:ServerThread
package com.crackj2ee.thread;
public class ServerThread extends Thread {
private boolean stop = false;
private Queue queue;
public ServerThread(Queue queue) {
this.queue = queue;
}
public void shutdown() {
stop = true;
this.interrupt();
try {
this.join();
}
catch(InterruptedException ie) {}
}
public void run() {
while(!stop) {
Request request = queue.getRequest();
System.out.println("[ServerThread] handle request: " + request);
try {
Thread.sleep(2000);
}
catch(InterruptedException ie) {}
}
System.out.println("[ServerThread] shutdown.");
}
}
服务器线E在U色部分可能会阻塞,也就是说QQueue.getRequest是一个阻塞方法。这和java标准库的许多IOҎ(gu)cM?/P>
最后,写一个Main来启动他们:
package com.crackj2ee.thread;
public class Main {
public static void main(String[] args) {
Queue queue = new Queue();
ServerThread server = new ServerThread(queue);
server.start();
ClientThread[] clients = new ClientThread[5];
for(int i=0; i<clients.length; i++) {
clients[i] = new ClientThread(queue, ""+i);
clients[i].start();
}
try {
Thread.sleep(100000);
}
catch(InterruptedException ie) {}
server.shutdown();
}
}
我们启动?个客L(fng)U程和一个服务器U程Q运行结果如下:
[ClientThread-0] send request: Request-4984
[ServerThread] handle request: Request-4984
[ClientThread-1] send request: Request-2020
[ClientThread-2] send request: Request-8980
[ClientThread-3] send request: Request-5044
[ClientThread-4] send request: Request-548
[ClientThread-4] send request: Request-6832
[ServerThread] handle request: Request-2020
[ServerThread] handle request: Request-8980
[ServerThread] handle request: Request-5044
[ServerThread] handle request: Request-548
[ClientThread-4] send request: Request-1681
[ClientThread-0] send request: Request-7859
[ClientThread-3] send request: Request-3926
[ServerThread] handle request: Request-6832
[ClientThread-2] send request: Request-9906
......
可以观察到ServerThread处理来自不同客户端的h?/P>
思?/FONT>
Q: 服务器线E的wait条gwhile(queue.size()==0)能否换成if(queue.size()==0)?
A: 在这个例子中可以Q因为服务器U程只有一个。但是,如果服务器线E有多个Q例如Web应用E序有多个线E处理ƈ发请求,q非常普遍)Q就会造成严重问题?/P>
Q: 能否用sleep(1000)代替wait()?
A: l对不可以。sleep()不会释放锁,因此sleep期间别的U程Ҏ(gu)没有办法调用getRequest()和putRequest()Q导致所有相关线E都被阻塞?/P>
Q: (Request)queue.remove(0)可以攑ֈsynchronized() {}块外面吗Q?/P>
A: 不可以。因为while()是测试queueQremove()是用queueQ两者是一个原子操作,不能攑֜synchronized外面?/P>
ȝ
多线E设计看似简单,实际上必非总l地考虑各种锁定/同步的条ӞE不心Q就可能出错。ƈ且,当线E较?yu)时Q很可能发现不了问题Q一旦问题出现又难以调试?/P>
所q的是,已有一些被验证q的模式可以供我们用,我们会l介l一些常用的多线E设计模式?BR>
前面谈了多线E应用程序能极大地改善用L(fng)应。例如对于一个Web应用E序Q每当一个用戯求服务器q接Ӟ服务器就可以启动一个新U程为用h务?/P>
然而,创徏和销毁线E本w就有一定的开销Q如果频J创建和销毁线E,CPU和内存开销׃可忽略,垃圾攉器还必须负担更多的工作。因此,U程池就是ؓ了避免频J创建和销毁线E?/P>
每当服务器接受了一个新的请求后Q服务器׃U程池中挑选一个等待的U程q执行请求处理。处理完毕后Q线Eƈ不结束,而是转ؓd状态再ơ被攑օU程池中。这样就避免了频J创建和销毁线E?/P>
Worker Pattern实现了类似线E池的功能。首先定义Task接口Q?/P>
package com.crackj2ee.thread;
public interface Task {
void execute();
}
U程负责执行execute()Ҏ(gu)。注意到d是由子类通过实现execute()Ҏ(gu)实现的,U程本nq不知道自己执行的Q务。它只负责运行一个耗时的execute()Ҏ(gu)?/P>
具体d由子cd玎ͼ我们定义了一个CalculateTask和一个TimerTaskQ?/P>
// CalculateTask.java
package com.crackj2ee.thread;
public class CalculateTask implements Task {
private static int count = 0;
private int num = count;
public CalculateTask() {
count++;
}
public void execute() {
System.out.println("[CalculateTask " + num + "] start...");
try {
Thread.sleep(3000);
}
catch(InterruptedException ie) {}
System.out.println("[CalculateTask " + num + "] done.");
}
}
// TimerTask.java
package com.crackj2ee.thread;
public class TimerTask implements Task {
private static int count = 0;
private int num = count;
public TimerTask() {
count++;
}
public void execute() {
System.out.println("[TimerTask " + num + "] start...");
try {
Thread.sleep(2000);
}
catch(InterruptedException ie) {}
System.out.println("[TimerTask " + num + "] done.");
}
}
以上d均简单的sleep若干U?/P>
TaskQueue实现了一个队列,客户端可以将h攑օ队列Q服务器U程可以从队列中取出dQ?/P>
package com.crackj2ee.thread;
import java.util.*;
public class TaskQueue {
private List queue = new LinkedList();
public synchronized Task getTask() {
while(queue.size()==0) {
try {
this.wait();
}
catch(InterruptedException ie) {
return null;
}
}
return (Task)queue.remove(0);
}
public synchronized void putTask(Task task) {
queue.add(task);
this.notifyAll();
}
}
l于C真正的WorkerThreadQ这是真正执行Q务的服务器线E:
package com.crackj2ee.thread;
public class WorkerThread extends Thread {
private static int count = 0;
private boolean busy = false;
private boolean stop = false;
private TaskQueue queue;
public WorkerThread(ThreadGroup group, TaskQueue queue) {
super(group, "worker-" + count);
count++;
this.queue = queue;
}
public void shutdown() {
stop = true;
this.interrupt();
try {
this.join();
}
catch(InterruptedException ie) {}
}
public boolean isIdle() {
return !busy;
}
public void run() {
System.out.println(getName() + " start.");
while(!stop) {
Task task = queue.getTask();
if(task!=null) {
busy = true;
task.execute();
busy = false;
}
}
System.out.println(getName() + " end.");
}
}
前面已经讲过Qqueue.getTask()是一个阻塞方法,服务器线E可能在此wait()一D|间。此外,W(xu)orkerThreadq有一个shutdownҎ(gu)Q用于安全结束线E?/P>
最后是ThreadPoolQ负责管理所有的服务器线E,q可以动态增加和减少U程敎ͼ
package com.crackj2ee.thread;
import java.util.*;
public class ThreadPool extends ThreadGroup {
private List threads = new LinkedList();
private TaskQueue queue;
public ThreadPool(TaskQueue queue) {
super("Thread-Pool");
this.queue = queue;
}
public synchronized void addWorkerThread() {
Thread t = new WorkerThread(this, queue);
threads.add(t);
t.start();
}
public synchronized void removeWorkerThread() {
if(threads.size()>0) {
WorkerThread t = (WorkerThread)threads.remove(0);
t.shutdown();
}
}
public synchronized void currentStatus() {
System.out.println("-----------------------------------------------");
System.out.println("Thread count = " + threads.size());
Iterator it = threads.iterator();
while(it.hasNext()) {
WorkerThread t = (WorkerThread)it.next();
System.out.println(t.getName() + ": " + (t.isIdle() ? "idle" : "busy"));
}
System.out.println("-----------------------------------------------");
}
}
currentStatus()Ҏ(gu)是ؓ了方便调试,打印出所有线E的当前状态?/P>
最后,Main负责完成main()Ҏ(gu)Q?/P>
package com.crackj2ee.thread;
public class Main {
public static void main(String[] args) {
TaskQueue queue = new TaskQueue();
ThreadPool pool = new ThreadPool(queue);
for(int i=0; i<10; i++) {
queue.putTask(new CalculateTask());
queue.putTask(new TimerTask());
}
pool.addWorkerThread();
pool.addWorkerThread();
doSleep(8000);
pool.currentStatus();
pool.addWorkerThread();
pool.addWorkerThread();
pool.addWorkerThread();
pool.addWorkerThread();
pool.addWorkerThread();
doSleep(5000);
pool.currentStatus();
}
private static void doSleep(long ms) {
try {
Thread.sleep(ms);
}
catch(InterruptedException ie) {}
}
}
main()一开始放入了20个TaskQ然后动态添加了一些服务线E,q定期打印线E状态,q行l果如下Q?/P>
worker-0 start.
[CalculateTask 0] start...
worker-1 start.
[TimerTask 0] start...
[TimerTask 0] done.
[CalculateTask 1] start...
[CalculateTask 0] done.
[TimerTask 1] start...
[CalculateTask 1] done.
[CalculateTask 2] start...
[TimerTask 1] done.
[TimerTask 2] start...
[TimerTask 2] done.
[CalculateTask 3] start...
-----------------------------------------------
Thread count = 2
worker-0: busy
worker-1: busy
-----------------------------------------------
[CalculateTask 2] done.
[TimerTask 3] start...
worker-2 start.
[CalculateTask 4] start...
worker-3 start.
[TimerTask 4] start...
worker-4 start.
[CalculateTask 5] start...
worker-5 start.
[TimerTask 5] start...
worker-6 start.
[CalculateTask 6] start...
[CalculateTask 3] done.
[TimerTask 6] start...
[TimerTask 3] done.
[CalculateTask 7] start...
[TimerTask 4] done.
[TimerTask 7] start...
[TimerTask 5] done.
[CalculateTask 8] start...
[CalculateTask 4] done.
[TimerTask 8] start...
[CalculateTask 5] done.
[CalculateTask 9] start...
[CalculateTask 6] done.
[TimerTask 9] start...
[TimerTask 6] done.
[TimerTask 7] done.
-----------------------------------------------
Thread count = 7
worker-0: idle
worker-1: busy
worker-2: busy
worker-3: idle
worker-4: busy
worker-5: busy
worker-6: busy
-----------------------------------------------
[CalculateTask 7] done.
[CalculateTask 8] done.
[TimerTask 8] done.
[TimerTask 9] done.
[CalculateTask 9] done.
仔细观察Q一开始只有两个服务器U程Q因此线E状态都是忙Q后来线E数增多Q?个线E中的两个状态变成idleQ说明处于wait()状态?/P>
思?/STRONG>Q本例的U程调度法其实Ҏ(gu)没有Q因个应用是围绕TaskQueue设计的,不是以Thread PoolZ心设计的。因此,Task调度取决于TaskQueue的getTask()Ҏ(gu)Q你可以改进q个Ҏ(gu)Q例如用优先队列,使优先高的d先被执行?/P>
如果所有的服务器线E都处于busy状态,则说明Q务繁忙,TaskQueue的队列越来越长,最l会D服务器内存耗尽。因此,可以限制 TaskQueue的等待Q务数Q超q最大长度就拒绝处理。许多Web服务器在用户hJ忙时就会拒l用PHTTP 503 SERVICE UNAVAILABLE
多线E读写同一个对象的数据是很普遍的,通常Q要避免d冲突Q必M证Q何时候仅有一个线E在写入Q有U程正在d的时候,写入操作必ȝ待。简单说Q就是要避免“写-写”冲H和“读-写”冲H。但是同时读是允许的Q因为“读-诠Z不冲突Q而且很安全?/P>
要实C上的ReadWriteLockQ简单的使用synchronized׃行,我们必须自己设计一个ReadWriteLockc,在读之前Q必d获得“读锁”,写之前,必须先获得“写锁”。D例说明:
DataHandler对象保存了一个可d的char[]数组Q?/P>
package com.crackj2ee.thread;
public class DataHandler {
// store data:
private char[] buffer = "AAAAAAAAAA".toCharArray();
private char[] doRead() {
char[] ret = new char[buffer.length];
for(int i=0; i<buffer.length; i++) {
ret[i] = buffer[i];
sleep(3);
}
return ret;
}
private void doWrite(char[] data) {
if(data!=null) {
buffer = new char[data.length];
for(int i=0; i<buffer.length; i++) {
buffer[i] = data[i];
sleep(10);
}
}
}
private void sleep(int ms) {
try {
Thread.sleep(ms);
}
catch(InterruptedException ie) {}
}
}
doRead()和doWrite()Ҏ(gu)是非U程安全的读写方法。ؓ了演C,加入了sleep()Qƈ讄ȝ速度大约是写?倍,q符合通常的情c?/P>
Z让多U程能安全读写,我们设计了一个ReadWriteLockQ?/P>
package com.crackj2ee.thread;
public class ReadWriteLock {
private int readingThreads = 0;
private int writingThreads = 0;
private int waitingThreads = 0; // waiting for write
private boolean preferWrite = true;
public synchronized void readLock() throws InterruptedException {
while(writingThreads>0 || (preferWrite && waitingThreads>0))
this.wait();
readingThreads++;
}
public synchronized void readUnlock() {
readingThreads--;
preferWrite = true;
notifyAll();
}
public synchronized void writeLock() throws InterruptedException {
waitingThreads++;
try {
while(readingThreads>0 || writingThreads>0)
this.wait();
}
finally {
waitingThreads--;
}
writingThreads++;
}
public synchronized void writeUnlock() {
writingThreads--;
preferWrite = false;
notifyAll();
}
}
readLock()用于获得读锁QreadUnlock()释放读锁QwriteLock()和writeUnlock()一栗由于锁用完必须释放Q因此,必须保证lock和unlock匚w。我们修改DataHandlerQ加入ReadWriteLockQ?/P>
package com.crackj2ee.thread;
public class DataHandler {
// store data:
private char[] buffer = "AAAAAAAAAA".toCharArray();
// lock:
private ReadWriteLock lock = new ReadWriteLock();
public char[] read(String name) throws InterruptedException {
System.out.println(name + " waiting for read...");
lock.readLock();
try {
char[] data = doRead();
System.out.println(name + " reads data: " + new String(data));
return data;
}
finally {
lock.readUnlock();
}
}
public void write(String name, char[] data) throws InterruptedException {
System.out.println(name + " waiting for write...");
lock.writeLock();
try {
System.out.println(name + " wrote data: " + new String(data));
doWrite(data);
}
finally {
lock.writeUnlock();
}
}
private char[] doRead() {
char[] ret = new char[buffer.length];
for(int i=0; i<buffer.length; i++) {
ret[i] = buffer[i];
sleep(3);
}
return ret;
}
private void doWrite(char[] data) {
if(data!=null) {
buffer = new char[data.length];
for(int i=0; i<buffer.length; i++) {
buffer[i] = data[i];
sleep(10);
}
}
}
private void sleep(int ms) {
try {
Thread.sleep(ms);
}
catch(InterruptedException ie) {}
}
}
publicҎ(gu)read()和write()完全装了底层的ReadWriteLockQ因此,多线E可以安全地调用q两个方法:
// ReadingThread不断d数据Q?BR>package com.crackj2ee.thread;
public class ReadingThread extends Thread {
private DataHandler handler;
public ReadingThread(DataHandler handler) {
this.handler = handler;
}
public void run() {
for(;;) {
try {
char[] data = handler.read(getName());
Thread.sleep((long)(Math.random()*1000+100));
}
catch(InterruptedException ie) {
break;
}
}
}
}
// WritingThread不断写入数据Q每ơ写入的都是10个相同的字符Q?BR>package com.crackj2ee.thread;
public class WritingThread extends Thread {
private DataHandler handler;
public WritingThread(DataHandler handler) {
this.handler = handler;
}
public void run() {
char[] data = new char[10];
for(;;) {
try {
fill(data);
handler.write(getName(), data);
Thread.sleep((long)(Math.random()*1000+100));
}
catch(InterruptedException ie) {
break;
}
}
}
// 产生一个A-Z随机字符Q填入char[10]:
private void fill(char[] data) {
char c = (char)(Math.random()*26+'A');
for(int i=0; i<data.length; i++)
data[i] = c;
}
}
最后Main负责启动q些U程Q?/P>
package com.crackj2ee.thread;
public class Main {
public static void main(String[] args) {
DataHandler handler = new DataHandler();
Thread[] ts = new Thread[] {
new ReadingThread(handler),
new ReadingThread(handler),
new ReadingThread(handler),
new ReadingThread(handler),
new ReadingThread(handler),
new WritingThread(handler),
new WritingThread(handler)
};
for(int i=0; i<ts.length; i++) {
ts[i].start();
}
}
}
我们启动?个读U程?个写U程Q运行结果如下:
Thread-0 waiting for read...
Thread-1 waiting for read...
Thread-2 waiting for read...
Thread-3 waiting for read...
Thread-4 waiting for read...
Thread-5 waiting for write...
Thread-6 waiting for write...
Thread-4 reads data: AAAAAAAAAA
Thread-3 reads data: AAAAAAAAAA
Thread-2 reads data: AAAAAAAAAA
Thread-1 reads data: AAAAAAAAAA
Thread-0 reads data: AAAAAAAAAA
Thread-5 wrote data: EEEEEEEEEE
Thread-6 wrote data: MMMMMMMMMM
Thread-1 waiting for read...
Thread-4 waiting for read...
Thread-1 reads data: MMMMMMMMMM
Thread-4 reads data: MMMMMMMMMM
Thread-2 waiting for read...
Thread-2 reads data: MMMMMMMMMM
Thread-0 waiting for read...
Thread-0 reads data: MMMMMMMMMM
Thread-4 waiting for read...
Thread-4 reads data: MMMMMMMMMM
Thread-2 waiting for read...
Thread-5 waiting for write...
Thread-2 reads data: MMMMMMMMMM
Thread-5 wrote data: GGGGGGGGGG
Thread-6 waiting for write...
Thread-6 wrote data: AAAAAAAAAA
Thread-3 waiting for read...
Thread-3 reads data: AAAAAAAAAA
......
可以看到Q每ơ读/写都是完整的原子操作Q因为我们每ơ写入的都是10个相同字W。ƈ且,每次d的都是最q一ơ写入的内容?/P>
如果LReadWriteLockQ?/P>
package com.crackj2ee.thread;
public class DataHandler {
// store data:
private char[] buffer = "AAAAAAAAAA".toCharArray();
public char[] read(String name) throws InterruptedException {
char[] data = doRead();
System.out.println(name + " reads data: " + new String(data));
return data;
}
public void write(String name, char[] data) throws InterruptedException {
System.out.println(name + " wrote data: " + new String(data));
doWrite(data);
}
private char[] doRead() {
char[] ret = new char[10];
for(int i=0; i<10; i++) {
ret[i] = buffer[i];
sleep(3);
}
return ret;
}
private void doWrite(char[] data) {
for(int i=0; i<10; i++) {
buffer[i] = data[i];
sleep(10);
}
}
private void sleep(int ms) {
try {
Thread.sleep(ms);
}
catch(InterruptedException ie) {}
}
}
q行l果如下Q?/P>
Thread-5 wrote data: AAAAAAAAAA
Thread-6 wrote data: MMMMMMMMMM
Thread-0 reads data: AAAAAAAAAA
Thread-1 reads data: AAAAAAAAAA
Thread-2 reads data: AAAAAAAAAA
Thread-3 reads data: AAAAAAAAAA
Thread-4 reads data: AAAAAAAAAA
Thread-2 reads data: MAAAAAAAAA
Thread-3 reads data: MAAAAAAAAA
Thread-5 wrote data: CCCCCCCCCC
Thread-1 reads data: MAAAAAAAAA
Thread-0 reads data: MAAAAAAAAA
Thread-4 reads data: MAAAAAAAAA
Thread-6 wrote data: EEEEEEEEEE
Thread-3 reads data: EEEEECCCCC
Thread-4 reads data: EEEEEEEEEC
Thread-1 reads data: EEEEEEEEEE
可以看到在Thread-6写入EEEEEEEEEE的过E中Q?个线E读取的内容是不同的?/P>
思?/STRONG>
java的synchronized提供了最底层的物理锁Q要在synchronized的基上,实现自己的逻辑锁,必Ml设计ReadWriteLock?/P>
Q: lock.readLock()Z么不攑օtry{ } 内?
A: 因ؓreadLock()会抛出InterruptedExceptionQ导致readingThreads++不执行,而readUnlock()?finally{ } 中,DreadingThreads--执行Q从而readingThread状态出错。writeLock()也是cM的?/P>
Q: preferWrite有用吗?
A: 如果LpreferWriteQ线E安全不受媄响。但是,如果dU程很多Q上一个线E还没有d完,下一个线E又开始读了,导致写入线E长旉无法获得writeLockQ如果写入线E等待的很多Q一个接一个写Q也会导致读取线E长旉无法获得readLock。preferWrite的作用是让读 /写交替执行,避免׃ȝE繁忙导致写无法q行和由于写U程J忙DL法进行?/P>
Q: notifyAll()换成notify()行不行?
A: 不可以。由于preferWrite的存在,如果一个线E刚d完毕Q此时preferWrite=trueQ再notify()Q若恰好唤醒的是一个读U程Q则while(writingThreads>0 || (preferWrite && waitingThreads>0))可能为trueD该读U程l箋{待Q而等待写入的U程也处于wait()中,l果所有线E都处于wait ()状态,谁也无法唤醒谁。因此,notifyAll()比notify()要来得安全。程序验证notify()带来的死锁:
Thread-0 waiting for read...
Thread-1 waiting for read...
Thread-2 waiting for read...
Thread-3 waiting for read...
Thread-4 waiting for read...
Thread-5 waiting for write...
Thread-6 waiting for write...
Thread-0 reads data: AAAAAAAAAA
Thread-4 reads data: AAAAAAAAAA
Thread-3 reads data: AAAAAAAAAA
Thread-2 reads data: AAAAAAAAAA
Thread-1 reads data: AAAAAAAAAA
Thread-5 wrote data: CCCCCCCCCC
Thread-2 waiting for read...
Thread-1 waiting for read...
Thread-3 waiting for read...
Thread-0 waiting for read...
Thread-4 waiting for read...
Thread-6 wrote data: LLLLLLLLLL
Thread-5 waiting for write...
Thread-6 waiting for write...
Thread-2 reads data: LLLLLLLLLL
Thread-2 waiting for read...
Q运行到此不动了Q?/FONT>
注意到这U死锁是׃所有线E都在等待别的线E唤醒自己,l果都无法醒q来。这和两个线E希望获得对方已有的锁造成死锁不同。因此多U程设计的难度远q高于单U程应用?/P>
b=(a=(b=(a^b))^a)^b 解答? a = a^(a^b); b = b^(a^b); |