﻿<?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-rainmanyang</title><link>http://www.blogjava.net/rainmanyang/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 28 Apr 2026 19:01:59 GMT</lastBuildDate><pubDate>Tue, 28 Apr 2026 19:01:59 GMT</pubDate><ttl>60</ttl><item><title>发现自己人性的弱点，从现在开始，想到就补充！</title><link>http://www.blogjava.net/rainmanyang/archive/2009/01/29/252675.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Wed, 28 Jan 2009 16:38:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/archive/2009/01/29/252675.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/252675.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/archive/2009/01/29/252675.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/252675.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/252675.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天和原来的老同学聚会回来，坐在沙发上看电视突然觉得有点失落。脑海里涌现出了过去生活中的一些场景，对同学以及朋友一些行为的蔑视，对家人关心的无视。在父亲的眼里我是他一生的骄傲，虽然在心里我深深爱着父亲，但是在行为中并没有给予他足够的尊重，我的一些自认为开玩笑的言语可能对父亲来说是一种伤害。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这一切的一切都来自源于我自认为还有些高的学历，这种自以为是没有帮助我在学业和人际上有所斩获，却危害到了与朋友与家人的关系。这个问题自己早早已经有所察觉，但都一直没有引以为戒。人贵自知，其实我知道自己并没有自己认为得那么优秀，但为了维护自己在别人面前的形象，给自己穿上了一个外壳。思绪有点乱，写的有点乱七八糟。古人一日一省，写了半天，今天首先总结我的第一个弱点：1.自以为是，有些时候过于自大。 
<img src ="http://www.blogjava.net/rainmanyang/aggbug/252675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2009-01-29 00:38 <a href="http://www.blogjava.net/rainmanyang/archive/2009/01/29/252675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>生命是一个长期积累的过程</title><link>http://www.blogjava.net/rainmanyang/archive/2008/12/22/247801.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Mon, 22 Dec 2008 10:01:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/archive/2008/12/22/247801.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/247801.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/archive/2008/12/22/247801.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/247801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/247801.html</trackback:ping><description><![CDATA[台湾新竹清华大学电机系&nbsp;彭明辉老师<br />
许多同学应该都还记得联考前夕的焦虑：差一分可能要掉好几个志愿，甚至于一生的命<br />
运从此改观！到了大四，这种焦虑可能更强烈而复杂：到底要先当兵，就业，还是先考<br />
研究所？我就经常碰到学生充满焦虑的问我这些问题。可是，这些焦虑实在是莫须有的<br />
！生命是一种长期而持续的累积过程，绝不会因为单一的事件而毁了一个人的一生，也<br />
不会因为单一的事件而救了一个人的一生。属于我们该得的，迟早会得到；属于我们不<br />
该得的，即使侥幸巧取也不可能长久保有。如果我们看清这个事实，许多所谓"人生的重<br />
大抉择就可以淡然处之，根本无需焦虑。而所谓"人生的困境"，也往往当下就变得无足<br />
挂齿。<br />
我自己就是一个活生生的例子。从一进大学就决定不再念研究所，所以，大学四年的时<br />
间多半在念人文科学的东西。毕业后工作了几年，才决定要念研究所。硕士毕业后，立<br />
下决心：从此不再为文凭而念书。谁知道，世事难料，当了五年讲师后，我又被时势所<br />
迫，出国念博士。&nbsp;<br />
出国时，一位大学同学笑我：全班最晚念博士的都要回国了，你现在才要出去？两年后<br />
&nbsp;我从剑桥回来，觉得人生际遇无常，莫此为甚：一个从大一就决定再也不钻营学位的人<br />
，竟然连硕士和博士都拿到了！属于我们该得的，哪样曾经少过？而人生中该得与不该<br />
得的究竟有多少，我们又何曾知晓？从此我对际遇一事不能不更加淡然。&nbsp;<br />
当讲师期间，有些态度较极端的学生会当面表现出他们的不屑；从剑桥回来时，却被学<br />
生当做不得了的事看待。这种表面上的大起大落，其实都是好事者之言，完全看不到事<br />
实的真相。从表面上看来，两年就拿到剑桥博士，这好像很了不起。但是，在这两年之<br />
前我已经花整整一年，将研究主题有关的论文全部看完，并找出研究方向；而之前更已<br />
花三年时间做控制方面的研究，并且在国际着名的学术期刊中发表论文。而从硕士毕业<br />
到拿博士，期间七年的时间我从不停止过研究与自修。&nbsp;所以，这个博士其实是累积了七<br />
年的成果，或者，只算我花在控制学门的时间，也至少有五年），根本也没什么好惊讶<br />
的。&nbsp;<br />
常人不从长期而持续的累积过程来看待生命因积蓄而有的成果，老爱在表面上以断裂而<br />
孤立的事件夸大议论，因此每每在平淡无奇的事件上强做悲喜。可是对我来讲，当讲师<br />
期间被学生瞧不起，以及剑桥刚回来时被同学夸大本事，都只是表象。事实是：我只在<br />
乎每天二十四小时点点滴滴的累积。&nbsp;<br />
拿硕士或博士只是特定时刻里这些成果累积的外在展示而已，人生命中真实的累积从不<br />
曾因这些事件而终止或添加。&nbsp;<br />
常有学生满怀忧虑的问我："老师，我很想先当完兵，工作一两年再考研究所。这样好吗<br />
？"&nbsp;<br />
"很好，这样子有机会先用实务来印证学理，你念研究所时会比别人了解自己要的是什么<br />
。"&nbsp;<br />
"可是，我怕当完兵又工作后，会失去斗志，因此考不上研究所。"&nbsp;<br />
"那你就先考研究所好了。"&nbsp;<br />
"可是，假如我先念研究所，我怕自己又会像念大学时一样茫然，因此念的不甘不愿的。<br />
"&nbsp;<br />
"那你还是先去工作好了！&nbsp;"&nbsp;<br />
"可是。。。。。。。&nbsp;<br />
我完全可以体会到他们的焦虑，可是却无法压抑住对于这种话的感慨。其实，说穿了他<br />
所需要的就是两年研究所加两年工作，以便加深知识的深广度和获取实务经验。&nbsp;<br />
先工作或先升学，表面上大相迳庭，其实骨子里的差别根本可以忽略。&nbsp;<br />
在"朝三暮四"这个成语故事里，主人原本喂养猴子的橡实是"早上四颗下午三颗"，后来<br />
改为"朝三暮四"，猴子就不高兴而坚持改回到"朝四暮三"。其实，先工作或先升学，期<br />
间差异就有如"朝三暮四"与"朝四暮三"，原不值得计较。但是，我们经常看不到这种生<br />
命过程中长远而持续的累积，老爱将一时际遇中的小差别夸大到生死攸关的地步。&nbsp;　&nbsp;<br />
最讽刺的是：当我们面对两个可能的方案，而焦虑得不知如何抉择时，通常表示这两个<br />
方案可能一样好，或者一样坏，因而实际上选择哪个都一样，唯一的差别只是先后之序<br />
而已。而且，愈是让我们焦虑得厉害的，其实差别越小，愈不值得焦虑。反而真正有明<br />
显的好坏差别时，我们轻易的就知道该怎么做了。可是我们却经常看不到长远的将来，<br />
短视的盯着两案短期内的得失：想选甲案，就舍不得乙案的好处；想选乙案，又舍不得<br />
甲案的好处。如果看得够远，人生长则八、九十，短则五、六十年，先做哪一件事又有<br />
什么关系？甚至当完兵又工作后，再花一整年准备研究所，又有什么了不起？当然，有<br />
些人还是会忧虑说："我当完兵又工作后，会不会因为家累或记忆力衰退而比较难考上研<br />
究所？"&nbsp;我只能这样回答："一个人考不上研究所，只有两个可能：或者他不够聪明，或<br />
者他的确够聪明。不够聪明而考不上，那也没什么好抱怨的。假如你够聪明，还考不上<br />
研究所，那只能说你的决心不够强。假如你是决心不够强，就表示你生命中还有其他的<br />
可能性，其重要程度并不下于硕士学位，而你舍不得丢下他。既然如此，考不上研究所<br />
也无须感到遗憾。不是吗？"人生的路这么多，为什么要老斤斤计较着一个可能性？&nbsp;<br />
我高中最要好的朋友，一生背运：高中考两次，高一念两次，大学又考两次，甚至连机<br />
车驾照都考两次。毕业后，他告诉自己：我没有关系，也没有学历，只能靠加倍的诚恳<br />
和努力。现在，他自己拥有一家公司，年收入数千万。&nbsp;<br />
一个人在升学过程中不顺利，而在事业上顺利，这是常见的事。有才华的人，不会因为<br />
被名校拒绝而连带失去他的才华，只不过要另外找适合他表现的场所而已。反过来，一<br />
个人在升学过程中太顺利，也难免因而放不下身段去创业，而只能乖乖领薪水过活。&nbsp;<br />
福兮祸兮，谁人知晓？&nbsp;我们又有什么好得意？又有什么好忧虑？人生的得与失，有时候<br />
怎么也说不清楚，有时候却再简单不过了：我们得到平日累积的成果，而失去我们不曾<br />
努力累积的！所以重要的不是和别人比成就，而是努力去做自己想做的。最后该得到的<br />
不会少你一分，不该得到的也不会多你一分。&nbsp;<br />
好像是前年的时候，我遇到一位高中同学。他在南加大当电机系的副教授，被清华电机<br />
聘回来开短期课程。从高中时代他就很用功，以第一志愿上台大电机后，四年都拿书卷<br />
奖，相信他在专业上的研究也已卓然有成。回想高中入学时，我们两个人的智力测验成<br />
绩分居全学年第一，第二名。可是从高一我就不曾放弃自己喜欢的文学，音乐，书法，<br />
艺术和哲学，而他却始终不曾分心，因此两个人在学术上的差距只会愈来愈远。反过来<br />
说，这十几二十年我在人文领域所获得的满足，恐怕已远非他能理解的了。我太太问过<br />
我，如果我肯全心专注于一个研究领域，是不是至少会赶上这位同学的成就？我不这样<br />
想，两个不同性情的人，注定要走两条不同的路。不该得的东西，我们注定是得不到的<br />
，随随便便拿两个人来比，只看到他所得到的，却看不到他所失去的，这有什么意义？<br />
&nbsp;<br />
有次清华电台访问我："老师你如何面对你人生中的困境？"我当场愣在那里，怎么样都<br />
想不出我这一生什么时候有过困境！后来仔细回想，才发现：我不是没有过困境，而是<br />
被常人当作"困境"的境遇，我都当作一时的际遇，不曾在意过而已。刚服完兵役时，长<br />
子已出生却还找不到工作。我曾焦虑过，却又觉得迟早会有工作，报酬也不至于低的离<br />
谱，不曾太放在心上。念硕士期间，家计全靠太太的薪水，省吃俭用，对我而言又算不<br />
上困境。一来精神上我过的很充实，二来我知道这一切是为了让自己有机会转行去教书<br />
(做自己想做的事)。三十一岁才要出国，而同学正要回系上任教，我很紧张(不知道剑桥<br />
要求的有多严)，却不曾丧气。因为，我知道自己过去一直很努力，也有很满意的心得和<br />
成果，只不过别人看不到而已。　　&nbsp;<br />
我没有过困境，因为我从不在乎外在的得失，也不武断的和别人比高下，而只在乎自己<br />
内在真实的累积。&nbsp;　　&nbsp;<br />
我没有过困境，因为我确实了解到：生命是一种长期而持续的累积过程，绝不会因为单<br />
一的事件而有剧烈的起伏。　　&nbsp;<br />
同时我也相信：属于我们该得的，迟早会得到；属于我们不该得的，即使一分也不可能<br />
增加。假如你可以持有相同的信念，那么人生于你也会是宽广而长远，没有什么了不得<br />
的"困境"，也没有什么好焦虑的了。
<img src ="http://www.blogjava.net/rainmanyang/aggbug/247801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-12-22 18:01 <a href="http://www.blogjava.net/rainmanyang/archive/2008/12/22/247801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>终于毕业了！</title><link>http://www.blogjava.net/rainmanyang/archive/2008/12/15/246315.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Mon, 15 Dec 2008 01:48:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/archive/2008/12/15/246315.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/246315.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/archive/2008/12/15/246315.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/246315.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/246315.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 经过最后3个小时的检查，终于下定决心将论文送进了打印店。看着几个月来的成果一张一张从机器里出来，心里的担子反而越来越重了，说实话，这篇论文确实漏洞百出，直到打印前的一个小时还检查出了很多错误。要说这篇论文是我学习两年来的研究成果，不如说是两个月更为合适，为了能有足够的时间找工作，暑假就匆匆把论文写完了，质量不言而喻。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天，本以为背了几个月的包袱终于可以卸下了，哪知道自己并没有想象得这么洒脱，看来真正的解脱要等到盲申结果出来后了，god bless me！<br />
<img src ="http://www.blogjava.net/rainmanyang/aggbug/246315.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-12-15 09:48 <a href="http://www.blogjava.net/rainmanyang/archive/2008/12/15/246315.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>纪念我的第一份offer</title><link>http://www.blogjava.net/rainmanyang/archive/2008/10/28/237087.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Tue, 28 Oct 2008 05:39:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/archive/2008/10/28/237087.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/237087.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/archive/2008/10/28/237087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/237087.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/237087.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 经过两个月的劳碌奔波，终于在上周拿到了人生的第一个offer。虽然公司已经并不像早几年那样那么令人向往，但总算工资等福利待遇还不错，工作强度也不大，听上届的师兄说各方面人事关系也很简单，同事都很好相处。对我这样二流院校毕业的小硕来说，能找到这样一份工作也算达到自己的要求了，毕竟全院通过笔试和面试最后拿到offer的只有我一个（哈哈，自己yy下了）。当然，08招聘的之路才算是进行到中途，后面还有很多自己心仪的公司，现在就算做个中期终结，希望以后的求职之路能够走得更好。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首次失败：08奥运是全球的一次重大体育盛典，我当然也不例外在它的陪伴下度过了炎热的8月，同时，我的研究生毕业论文也在这段时间完成了初稿（后来证实需要做重大修改<span style="font-size: 18pt">orz</span>）。得益于论文完成得比较早，使我可以有足够的时间来参加校园招聘的宣讲会和笔试，面试。我的第一场面试是上海亚信，去的那天天上下着小雨，似乎我人生中的每次转折都伴随着雨天，中考，高考，考研无不都是阴雨绵绵。这次面试地点不是在亚信总部，而是在移动的客服中心。管理得相当严格，过了好几道门禁才进入他们办公的地方，第一感觉就是一个临时办公地点，几张桌子拼成一张大的办工作，零零散散有人坐在那办公。带我进门的人随便问了我几句学历情况，当知道我还没毕业就表现的比较吃惊（究竟有没有仔细看我的简历），我明确表示可以先实习，看样子他对我的兴趣就降低了很多，先拿了份内部的笔试卷子让我做，大概有4页，前两张都是数据库的题目，大部分是比较基础的，可我也做不来，那时候对数据库还没有系统复习，也应为这，我后一个月看了好几本oracle的书，算是有了很大的进步。后半部分是java题，做的比较顺，js的题做得不怎么样。做完后把题目交给带我上来的员工，他随便看了两下就说做的还不错，随便和我blabla了几句就让我回去等消息了。第一次面试就这样以失败告终了，事后总结估计是说我还没毕业只能实习就被bs了，这不能怪我吧，简历上说的很清楚了，下次接到电话要先和对方确认清楚再去，免得白跑一趟浪费感情。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在支付宝的两次失败：9月份开始一些大公司的校园招聘就陆陆续续开始了，我也结束了在51job和chinahr漫天撒网，着重于校招公司的网申。阿里系的几家公司开始得都比较早，最早开始的就是支付宝，也是我参加的第一场宣讲会。由于自己接触过支付平台的开发，对第三方支付平台的前景比较看好，所以对这次机会特别看重。总的来说支付宝的宣讲会还是比较成功的，前面的那位技术主管说话很有煽动力，很快把大家的兴趣调动起来了，短短半个多小时的演讲使我对第三方支付的现状和前景有了更深刻的认识。第二位hr主管的口才更是没得说，将会场带入了另一个高潮，总的来说这次宣讲给我的感觉非常棒，相比较来说我后面参加的网易，百度等大公司的宣讲就要逊色得多了，听到一半就有想睡觉的感觉，毫无激情。接下来的笔试感觉也比较顺利，大部分都有了解，就是css，和js忘得差不多了，最后两题乱写了一通。回到学校已经12点多了，这次算是初尝找工作的艰辛。晚上睡觉一直看着手机，生怕漏接通知我去面试的电话，谁知天意弄人，在我昏昏沉沉要睡着时手机竟然没电关机了，毫无察觉的我一直等得实在撑不住了才进入梦乡。早上破天荒的6点就醒来了，失望的拿起枕边的手机才发现已经关机了，忙插上充电器打开手机盯着看了五分多钟终于等来了那一条折腾了我一晚的短信，果真是通知我去面试的。以最快的速度打点完毕，带上简历直奔浙大，地方不好找，很小的一个入口，进去已经有不少人在门口填表了，一面一个大教室里坐了不少人在面试。在门口等了大约半个小时就有人叫我的名字了，被带到了比较靠里的一张桌子前，面试官看面相相当和蔼，一直对着我微笑。后来才知道这家伙是支付宝上海的技术主管，后来一次在支付宝的面试二面也栽在了他手上。第一次的面试确实在准备上也相当不充分，很多问题回答的都不是很好，答案都很简短，没有深入到细节，可能给他的映像就是我的知识结构都只是停留在表层。事后总结，涉及到技术的问题一定要展开说，以他的问题为核心辐射开来，这样才能占据主动。他如果对你所说的内容感兴趣的话会继续问这方面的问题，如果你的回答只是了了几句，那他会有别的准备好的问题来提问，这些问题很可能是你没有涉及到的内容。最后他也问了一些rp问题，问了我在校期间当学生干部的一些情况，我当时居然告诉他说我当学生干部是被迫的，是因为没人肯干才硬顶上的。当天最大的败笔就在此，做事如此不积极主动的人怎么让别人相信你能承担起一定的责任，所以在后面的面试中只要有rp方面的问题我的回答一定是要表现自己积极主动的一面，效果非常不错。支付宝的第一次面试一面就被打发回去了，但我后面面试经验大多是对这次面试中出现的问题总结得出的，可以说这次失败是这么多次失败中最有价值的一次。
<img src ="http://www.blogjava.net/rainmanyang/aggbug/237087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-28 13:39 <a href="http://www.blogjava.net/rainmanyang/archive/2008/10/28/237087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ClassLoader</title><link>http://www.blogjava.net/rainmanyang/articles/234518.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Wed, 15 Oct 2008 13:04:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/234518.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/234518.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/234518.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/234518.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/234518.html</trackback:ping><description><![CDATA[<p>关于Class的是如何加载的一直都很模糊，也没去怎么管它，昨天在参加网易的笔试时提到了这个问题。到网上查了查：</p>
<p>下面这一篇文章是JavaEYE里的：http://www.javaeye.com/topic/83978?page=1</p>
<p>ClassLoader一个经常出现又让很多人望而却步的词，本文将试图以最浅显易懂的方式来讲解 ClassLoader，希望能对不了解该机制的朋友起到一点点作用。 <br />
<br />
要深入了解ClassLoader，首先就要知道ClassLoader是用来干什么的，顾名思义，它就是用来加载Class文件到JVM，以供程序使用的。我们知道，java程序可以动态加载类定义，而这个动态加载的机制就是通过ClassLoader来实现的，所以可想而知ClassLoader的重要性如何。 <br />
<br />
看到这里，可能有的朋友会想到一个问题，那就是既然ClassLoader是用来加载类到JVM中的，那么ClassLoader又是如何被加载呢？难道它不是java的类？ <br />
<br />
没有错，在这里确实有一个ClassLoader不是用java语言所编写的，而是JVM实现的一部分，这个ClassLoader就是bootstrap classloader（启动类加载器），这个ClassLoader在JVM运行的时候加载java核心的API以满足java程序最基本的需求，其中就包括用户定义的ClassLoader，这里所谓的用户定义是指通过java程序实现的ClassLoader，一个是ExtClassLoader，这个ClassLoader是用来加载java的扩展API的，也就是/lib/ext中的类，一个是AppClassLoader，这个ClassLoader是用来加载用户机器上CLASSPATH设置目录中的Class的，通常在没有指定ClassLoader的情况下，程序员自定义的类就由该ClassLoader进行加载。 <br />
<br />
当运行一个程序的时候，JVM启动，运行bootstrap classloader，该ClassLoader加载java核心API（ExtClassLoader和AppClassLoader也在此时被加载），然后调用ExtClassLoader加载扩展API，最后AppClassLoader加载CLASSPATH目录下定义的Class，这就是一个程序最基本的加载流程。 <br />
<br />
上面大概讲解了一下ClassLoader的作用以及一个最基本的加载流程，接下来将讲解一下ClassLoader加载的方式，这里就不得不讲一下ClassLoader在这里使用了双亲委托模式进行类加载。 <br />
<br />
每一个自定义ClassLoader都必须继承ClassLoader这个抽象类，而每个ClassLoader都会有一个parent ClassLoader，我们可以看一下ClassLoader这个抽象类中有一个getParent()方法，这个方法用来返回当前ClassLoader的parent，注意，这个parent不是指的被继承的类，而是在实例化该ClassLoader时指定的一个ClassLoader，如果这个parent为null，那么就默认该ClassLoader的parent是bootstrap classloader，这个parent有什么用呢？ <br />
<br />
我们可以考虑这样一种情况，假设我们自定义了一个ClientDefClassLoader，我们使用这个自定义的ClassLoader加载java.lang.String，那么这里String是否会被这个ClassLoader加载呢？事实上java.lang.String这个类并不是被这个ClientDefClassLoader加载，而是由bootstrap classloader进行加载，为什么会这样？实际上这就是双亲委托模式的原因，因为在任何一个自定义ClassLoader加载一个类之前，它都会先委托它的父亲ClassLoader进行加载，只有当父亲ClassLoader无法加载成功后，才会由自己加载，在上面这个例子里，因为java.lang.String是属于java核心API的一个类，所以当使用ClientDefClassLoader加载它的时候，该ClassLoader会先委托它的父亲ClassLoader进行加载，上面讲过，当ClassLoader的parent为null时，ClassLoader的parent就是bootstrap classloader，所以在ClassLoader的最顶层就是bootstrap classloader，因此最终委托到bootstrap classloader的时候，bootstrap classloader就会返回String的Class。 <br />
<br />
我们来看一下ClassLoader中的一段源代码：</p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">synchronized</span><span style="color: #000000">&nbsp;Class&nbsp;loadClass(String&nbsp;name,&nbsp;</span><span style="color: #0000ff">boolean</span><span style="color: #000000">&nbsp;resolve)&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;2</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;ClassNotFoundException&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;3</span><span style="color: #000000"><img id="Codehighlighter1_111_690_Open_Image" onclick="this.style.display='none'; Codehighlighter1_111_690_Open_Text.style.display='none'; Codehighlighter1_111_690_Closed_Image.style.display='inline'; Codehighlighter1_111_690_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_111_690_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_111_690_Closed_Text.style.display='none'; Codehighlighter1_111_690_Open_Image.style.display='inline'; Codehighlighter1_111_690_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span id="Codehighlighter1_111_690_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_111_690_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;4</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;首先检查该name指定的class是否有被加载&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000"><br />
</span><span style="color: #008080">&nbsp;5</span><span style="color: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #000000">Class&nbsp;c&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;findLoadedClass(name);&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;6</span><span style="color: #000000"><img id="Codehighlighter1_200_615_Open_Image" onclick="this.style.display='none'; Codehighlighter1_200_615_Open_Text.style.display='none'; Codehighlighter1_200_615_Closed_Image.style.display='inline'; Codehighlighter1_200_615_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_200_615_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_200_615_Closed_Text.style.display='none'; Codehighlighter1_200_615_Open_Image.style.display='inline'; Codehighlighter1_200_615_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(c&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">)&nbsp;</span><span id="Codehighlighter1_200_615_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_200_615_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;7</span><span style="color: #000000"><img id="Codehighlighter1_214_472_Open_Image" onclick="this.style.display='none'; Codehighlighter1_214_472_Open_Text.style.display='none'; Codehighlighter1_214_472_Closed_Image.style.display='inline'; Codehighlighter1_214_472_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_214_472_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_214_472_Closed_Text.style.display='none'; Codehighlighter1_214_472_Open_Image.style.display='inline'; Codehighlighter1_214_472_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">try</span><span style="color: #000000">&nbsp;</span><span id="Codehighlighter1_214_472_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_214_472_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;8</span><span style="color: #000000"><img id="Codehighlighter1_244_345_Open_Image" onclick="this.style.display='none'; Codehighlighter1_244_345_Open_Text.style.display='none'; Codehighlighter1_244_345_Closed_Image.style.display='inline'; Codehighlighter1_244_345_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_244_345_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_244_345_Closed_Text.style.display='none'; Codehighlighter1_244_345_Open_Image.style.display='inline'; Codehighlighter1_244_345_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(parent&nbsp;</span><span style="color: #000000">!=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">)&nbsp;</span><span id="Codehighlighter1_244_345_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_244_345_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">&nbsp;9</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">如果parent不为null，则调用parent的loadClass进行加载&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000"><br />
</span><span style="color: #008080">10</span><span style="color: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;parent.loadClass(name,&nbsp;</span><span style="color: #0000ff">false</span><span style="color: #000000">);&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">11</span><span style="color: #000000"><img id="Codehighlighter1_352_461_Open_Image" onclick="this.style.display='none'; Codehighlighter1_352_461_Open_Text.style.display='none'; Codehighlighter1_352_461_Closed_Image.style.display='inline'; Codehighlighter1_352_461_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_352_461_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_352_461_Closed_Text.style.display='none'; Codehighlighter1_352_461_Open_Image.style.display='inline'; Codehighlighter1_352_461_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000">&nbsp;</span><span id="Codehighlighter1_352_461_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_352_461_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">12</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">parent为null，则调用BootstrapClassLoader进行加载&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000"><br />
</span><span style="color: #008080">13</span><span style="color: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;findBootstrapClass0(name);&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">14</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">15</span><span style="color: #000000"><img id="Codehighlighter1_507_609_Open_Image" onclick="this.style.display='none'; Codehighlighter1_507_609_Open_Text.style.display='none'; Codehighlighter1_507_609_Closed_Image.style.display='inline'; Codehighlighter1_507_609_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_507_609_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_507_609_Closed_Text.style.display='none'; Codehighlighter1_507_609_Open_Image.style.display='inline'; Codehighlighter1_507_609_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">catch</span><span style="color: #000000">&nbsp;(ClassNotFoundException&nbsp;e)&nbsp;</span><span id="Codehighlighter1_507_609_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_507_609_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">16</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">如果仍然无法加载成功，则调用自身的findClass进行加载&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000"><br />
</span><span style="color: #008080">17</span><span style="color: #008000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;findClass(name);&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">18</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">19</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">20</span><span style="color: #000000"><img id="Codehighlighter1_634_666_Open_Image" onclick="this.style.display='none'; Codehighlighter1_634_666_Open_Text.style.display='none'; Codehighlighter1_634_666_Closed_Image.style.display='inline'; Codehighlighter1_634_666_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"  alt="" /><img id="Codehighlighter1_634_666_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_634_666_Closed_Text.style.display='none'; Codehighlighter1_634_666_Open_Image.style.display='inline'; Codehighlighter1_634_666_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(resolve)&nbsp;</span><span id="Codehighlighter1_634_666_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_634_666_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">21</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resolveClass(c);&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">22</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"  alt="" />}</span></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">23</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top"  alt="" /></span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;c;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">24</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">25</span><span style="color: #000000"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<br />
从上面一段代码中，我们可以看出一个类加载的大概过程与之前我所举的例子是一样的，而我们要实现一个自定义类的时候，只需要实现findClass方法即可。 <br />
<br />
为什么要使用这种双亲委托模式呢？ <br />
<br />
第一个原因就是因为这样可以避免重复加载，当父亲已经加载了该类的时候，就没有必要子ClassLoader再加载一次。 <br />
<br />
第二个原因就是考虑到安全因素，我们试想一下，如果不使用这种委托模式，那我们就可以随时使用自定义的String来动态替代java核心api中定义类型，这样会存在非常大的安全隐患，而双亲委托的方式，就可以避免这种情况，因为String已经在启动时被加载，所以用户自定义类是无法加载一个自定义的ClassLoader。 <br />
<br />
上面对ClassLoader的加载机制进行了大概的介绍，接下来不得不在此讲解一下另外一个和ClassLoader相关的类，那就是Class类，每个被ClassLoader加载的class文件，最终都会以Class类的实例被程序员引用，我们可以把Class类当作是普通类的一个模板，JVM根据这个模板生成对应的实例，最终被程序员所使用。 <br />
<br />
我们看到在Class类中有个静态方法forName，这个方法和ClassLoader中的loadClass方法的目的一样，都是用来加载class的，但是两者在作用上却有所区别。 <br />
Class&lt;?&gt; loadClass(String name) <br />
Class&lt;?&gt; loadClass(String name, boolean resolve) <br />
我们看到上面两个方法声明，第二个方法的第二个参数是用于设置加载类的时候是否连接该类，true就连接，否则就不连接。 <br />
<br />
说到连接，不得不在此做一下解释，在JVM加载类的时候，需要经过三个步骤，装载、连接、初始化。装载就是找到相应的class文件，读入JVM，初始化就不用说了，最主要就说说连接。 <br />
<br />
连接分三步，第一步是验证class是否符合规格，第二步是准备，就是为类变量分配内存同时设置默认初始值，第三步就是解释，而这步就是可选的，根据上面loadClass方法的第二个参数来判定是否需要解释，所谓的解释根据《深入JVM》这本书的定义就是根据类中的符号引用查找相应的实体，再把符号引用替换成一个直接引用的过程。有点深奥吧，呵呵，在此就不多做解释了，想具体了解就翻翻《深入JVM吧》，呵呵，再这样一步步解释下去，那就不知道什么时候才能解释得完了。 <br />
<br />
我们再来看看那个两个参数的loadClass方法，在JAVA API 文档中，该方法的定义是protected，那也就是说该方法是被保护的，而用户真正应该使用的方法是一个参数的那个，一个参数的loadclass方法实际上就是调用了两个参数的方法，而第二个参数默认为false，因此在这里可以看出通过loadClass加载类实际上就是加载的时候并不对该类进行解释，因此也不会初始化该类。而Class类的forName方法则是相反，使用forName加载的时候就会将Class进行解释和初始化，forName也有另外一个版本的方法，可以设置是否初始化以及设置ClassLoader，在此就不多讲了。 <br />
<br />
不知道上面对这两种加载方式的解释是否足够清楚，就在此举个例子吧，例如JDBC DRIVER的加载，我们在加载JDBC驱动的时候都是使用的forName而非是ClassLoader的loadClass方法呢？我们知道，JDBC驱动是通过DriverManager，必须在DriverManager中注册，如果驱动类没有被初始化，则不能注册到DriverManager中，因此必须使用forName而不能用loadClass。 <br />
<br />
通过ClassLoader我们可以自定义类加载器，定制自己所需要的加载方式，例如从网络加载，从其他格式的文件加载等等都可以，其实ClassLoader还有很多地方没有讲到，例如ClassLoader内部的一些实现等等，本来希望能够讲得简单易懂一点，可是结果自己看回头好像感觉并不怎么样，郁闷，看来自己的文笔还是差太多了，希望能够给一些有需要的朋友一点帮助吧。
<p>另外一篇是：http://dev.csdn.net/article/68/68103.shtm</p>
<p><strong>静态库、动态连接库</strong></p>
<p>程序编制一般需经编辑、编译、连接、加载和运行几个步骤。在我们的应用中，有一些公共代码是需要反复使用，就把这些代码编译为&#8220;库&#8221;文件；在连接步骤中，连接器将从库文件取得所需的代码，复制到生成的可执行文件中。这种库称为静态库，其特点是可执行文件中包含了库代码的一份完整拷贝；缺点就是被多次使用就会有多份冗余拷贝。</p>
<p>为了克服这个缺点可以采用动态连接库。这个时候连接器仅仅是在可执行文件中打上标志，说明需要使用哪些动态连接库；当运行程序时，加载器根据这些标志把所需的动态连接库加载到内存。</p>
<p>另外在当前的编程环境中，一般都提供方法让程序在运行的时候把某个特定的动态连接库加载并运行，也可以将其卸载（例如Win32的LoadLibrary()&amp;FreeLibrary()和Posix的dlopen()&amp;dlclose()）。这个功能被广泛地用于在程序运行时刻更新某些功能模块或者是程序外观。</p>
<p><strong>What is ClassLoader?</strong></p>
<p>与普通程序不同的是，Java程序（class文件）并不是本地的可执行程序。当运行Java程序时，首先运行JVM（Java虚拟机），然后再把Java class加载到JVM里头运行，负责加载Java class的这部分就叫做Class Loader。</p>
<p>JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader，和JVM一样，Bootstrap ClassLoader是用本地代码实现的，它负责加载核心Java Class（即所有java.*开头的类）。另外JVM还会提供两个ClassLoader，它们都是用Java语言编写的，由Bootstrap ClassLoader加载；其中Extension ClassLoader负责加载扩展的Java class（例如所有javax.*开头的类和存放在JRE的ext目录下的类），Application ClassLoader负责加载应用程序自身的类。</p>
<p><strong>When to load the class?</strong></p>
<p>什么时候JVM会使用ClassLoader加载一个类呢？当你使用java去执行一个类，JVM使用Application ClassLoader加载这个类；然后如果类A引用了类B，不管是直接引用还是用Class.forName()引用，JVM就会找到加载类A的ClassLoader，并用这个ClassLoader来加载类B。</p>
<p><strong>Why use your own ClassLoader?</strong></p>
<p>似乎JVM自身的ClassLoader已经足够了，为什么我们还需要创建自己的ClassLoader呢？</p>
<p>因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件，如果编写你自己的ClassLoader，你可以做到：<br />
1）在执行非置信代码之前，自动验证数字签名<br />
2）动态地创建符合用户特定需要的定制化构建类<br />
3）从特定的场所取得java class，例如数据库中<br />
4) 等等</p>
<p>事实上当使用Applet的时候，就用到了特定的ClassLoader，因为这时需要从网络上加载java class，并且要检查相关的安全信息。</p>
<p>目前的应用服务器大都使用了ClassLoader技术，即使你不需要创建自己的ClassLoader，了解其原理也有助于更好地部署自己的应用。</p>
<p><strong>ClassLoader Tree &amp; Delegation Model</strong></p>
<p>当你决定创建你自己的ClassLoader时，需要继承java.lang.ClassLoader或者它的子类。在实例化每个ClassLoader对象时，需要指定一个父对象；如果没有指定的话，系统自动指定ClassLoader.getSystemClassLoader()为父对象。如下图：</p>
<img height="377" alt="" src="http://www.blogjava.net/images/blogjava_net/rainmanyang/untitled.jpg" width="477" border="0" /><br />
<p>在Java 1.2后，java class的加载采用所谓的委托模式（Delegation Modle），当调用一个ClassLoader.loadClass()加载一个类的时候，将遵循以下的步骤：<br />
1）检查这个类是否已经被加载进来了？<br />
2）如果还没有加载，调用父对象加载该类<br />
3）如果父对象无法加载，调用本对象的findClass()取得这个类。</p>
<p>所以当创建自己的Class Loader时，只需要重载findClass()这个方法。</p>
<p><strong>Unloading? Reloading?</strong></p>
<p>当一个java class被加载到JVM之后，它有没有可能被卸载呢？我们知道Win32有FreeLibrary()函数，Posix有dlclose()函数可以被调用来卸载指定的动态连接库，但是Java并没有提供一个UnloadClass()的方法来卸载指定的类。</p>
<p>在Java中，java class的卸载仅仅是一种对系统的优化，有助于减少应用对内存的占用。既然是一种优化方法，那么就完全是JVM自行决定如何实现，对Java开发人员来说是完全透明的。</p>
<p>在什么时候一个java class/interface会被卸载呢？Sun公司的<a href="http://java.sun.com/docs/books/jls/unloading-rationale.html"><font color="#4a664d">原话</font></a>是这么说的："class or interface may be unloaded if and only if its class loader is unreachable. Classes loaded by the bootstrap loader may not be unloaded."</p>
<p>事实上我们关心的不是如何卸载类的，我们关心的是如何更新已经被加载了的类从而更新应用的功能。JSP则是一个非常典型的例子，如果一个JSP文件被更改了，应用服务器则需要把更改后的JSP重新编译，然后加载新生成的类来响应后继的请求。</p>
<p>其实一个已经加载的类是无法被更新的，如果你试图用同一个ClassLoader再次加载同一个类，就会得到异常（java.lang.LinkageError: duplicate class definition），我们只能够重新创建一个新的ClassLoader实例来再次加载新类。至于原来已经加载的类，开发人员不必去管它，因为它可能还有实例正在被使用，只要相关的实例都被内存回收了，那么JVM就会在适当的时候把不会再使用的类卸载。</p>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/234518.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-15 21:04 <a href="http://www.blogjava.net/rainmanyang/articles/234518.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正确使用 Volatile 变量</title><link>http://www.blogjava.net/rainmanyang/articles/232766.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Mon, 06 Oct 2008 13:30:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232766.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232766.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232766.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232766.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: java&#8482; 语言包含两种内在的同步机制：同步块（或方法）和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差（但有时它更简单并且开销更低），而且其使用也更容易出错。在这期的&nbsp;Java 理论与实践&nbsp;中，Brian Goetz 将介绍几种正确使用 volatile 变量的模式，并针对其适用性限制提出一些建议。...&nbsp;&nbsp;<a href='http://www.blogjava.net/rainmanyang/articles/232766.html'>阅读全文</a><img src ="http://www.blogjava.net/rainmanyang/aggbug/232766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-06 21:30 <a href="http://www.blogjava.net/rainmanyang/articles/232766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ThreadLocal与synchronize</title><link>http://www.blogjava.net/rainmanyang/articles/232633.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Mon, 06 Oct 2008 04:13:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232633.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232633.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232633.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232633.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232633.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉，而对ThreadLocal就要陌生得多了。&nbsp;并发问题。当一个对象被两个线程同时访问时，可能有一个线程会得到不可预期的结果。&nbsp;一...&nbsp;&nbsp;<a href='http://www.blogjava.net/rainmanyang/articles/232633.html'>阅读全文</a><img src ="http://www.blogjava.net/rainmanyang/aggbug/232633.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-06 12:13 <a href="http://www.blogjava.net/rainmanyang/articles/232633.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用dom4j解析XML</title><link>http://www.blogjava.net/rainmanyang/articles/232558.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Sun, 05 Oct 2008 13:30:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232558.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232558.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232558.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232558.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232558.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: om4j 是一种解析 XML 文档的开放源代码 XML 框架。本文介绍如何使用包含在 dom4j 中的解析器创建并修改 XML 文档。dom4j API 包含一个解析 XML 文档的工具。本文中将使用这个解析器创建一个示例 XML 文档。清单 1 显示了这个示例 XML 文档，catalog.xml。清单 1. 示例 XML 文档（catalog.xml）&nbsp;&lt;?xml&...&nbsp;&nbsp;<a href='http://www.blogjava.net/rainmanyang/articles/232558.html'>阅读全文</a><img src ="http://www.blogjava.net/rainmanyang/aggbug/232558.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-05 21:30 <a href="http://www.blogjava.net/rainmanyang/articles/232558.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再谈ReentrantLock</title><link>http://www.blogjava.net/rainmanyang/articles/232227.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Fri, 03 Oct 2008 09:55:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232227.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232227.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232227.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232227.html</trackback:ping><description><![CDATA[<span style="font-family: Tahoma; font-size: 12px; line-height: 18px; ">
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span class="first-letter">重</span>入锁（<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>）是一种递归无阻塞的同步机制。以前一直认为它是synchronized的简单替代，而且实现机制也不相差太远。不过最近实践过程中发现它们之间还是有着天壤之别。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">以下是<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html" target="_blank" style="color: #006699; text-decoration: underline; ">官方说明</a>：一个可重入的互斥锁定 Lock，它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义，但功能更强大。<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span> 将由最近成功获得锁定，并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时，调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定，此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">它提供了lock()方法：<br />
如果该锁定没有被另一个线程保持，则获取该锁定并立即返回，将锁定的保持计数设置为 1。<br />
如果当前线程已经保持该锁定，则将保持计数加 1，并且该方法立即返回。<br />
如果该锁定被另一个线程保持，则出于线程调度的目的，禁用当前线程，并且在获得锁定之前，该线程将一直处于休眠状态，此时锁定保持计数被设置为 1。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">最近在研究<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-tree.html" target="_blank" style="color: #006699; text-decoration: underline; ">Java concurrent</a>中关于任务调度的实现时，读了延迟队列DelayQueue的一些代码，比如take()。该方法的主要功能是从优先队列（PriorityQueue）取出一个最应该执行的任务（最优值），如果该任务的预订执行时间未到，则需要wait这段时间差。反之，如果时间到了，则返回该任务。而offer()方法是将一个任务添加到该队列中。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">后来产生了一个疑问：如果最应该执行的任务是一个小时后执行的，而此时需要提交一个10秒后执行的任务，会出现什么状况？还是先看看take()的源代码：</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> E take() </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ReentrantLock lock </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.lock;<br />
<br />
lock.lockInterruptibly();<br />
<br />
</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "> {<br />
<br />
</span><span style="color: #0000FF; ">for</span><span style="color: #000000; "> (;;) {<br />
<br />
E first </span><span style="color: #000000; ">=</span><span style="color: #000000; "> q.peek();<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (first </span><span style="color: #000000; ">==</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">) {<br />
<br />
available.await();<br />
<br />
} </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "> {<br />
<br />
</span><span style="color: #0000FF; ">long</span><span style="color: #000000; "> delay </span><span style="color: #000000; ">=</span><span style="color: #000000; ">  first.getDelay(TimeUnit.NANOSECONDS);<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (delay </span><span style="color: #000000; ">></span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">) {<br />
<br />
</span><span style="color: #0000FF; ">long</span><span style="color: #000000; "> tl </span><span style="color: #000000; ">=</span><span style="color: #000000; "> available.awaitNanos(delay);<br />
<br />
} </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "> {<br />
<br />
E x </span><span style="color: #000000; ">=</span><span style="color: #000000; "> q.poll();<br />
<br />
</span><span style="color: #0000FF; ">assert</span><span style="color: #000000; "> x </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (q.size() </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />
<br />
available.signalAll(); </span><span style="color: #008000; ">//</span><span style="color: #008000; "> wake up other takers</span><span style="color: #008000; "><br />
</span><span style="color: #000000; "><br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> x;<br />
<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
} </span><span style="color: #0000FF; ">finally</span><span style="color: #000000; "> {<br />
<br />
lock.unlock();<br />
<br />
}<br />
<br />
}</span></div>
<p>而以下是offer()的源代码:</p>
<p>
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public<span style="color: #000000; "> </span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; "> offer(E e) {<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ReentrantLock lock </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.lock;<br />
<br />
lock.lock();<br />
<br />
</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "> {<br />
<br />
E first </span><span style="color: #000000; ">=</span><span style="color: #000000; "> q.peek();<br />
<br />
q.offer(e);<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (first </span><span style="color: #000000; ">==</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; "> </span><span style="color: #000000; ">||</span><span style="color: #000000; "> e.compareTo(first) </span><span style="color: #000000; ">&lt;</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />
<br />
available.signalAll();<br />
<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />
<br />
} </span><span style="color: #0000FF; ">finally</span><span style="color: #000000; "> {<br />
<br />
lock.unlock();<br />
<br />
}<br />
<br />
}</span></span></div>
</span>
<p><span style="font-family: Tahoma; font-size: 12px; line-height: 18px; ">
</span></p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">如代码所示，take()和offer()都是lock了重入锁。如果按照synchronized的思维（使用诸如synchronized(obj)的方法），这两个方法是互斥的。回到刚才的疑问，take()方法需要等待1个小时才能返回，而offer()需要马上提交一个10秒后运行的任务，会不会一直等待take()返回后才能提交呢？答案是否定的，通过编写验证代码也说明了这一点。这让我对重入锁有了更大的兴趣，它确实是一个无阻塞的锁。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">下面的代码也许能说明问题：运行了4个线程，每一次运行前打印lock的当前状态。运行后都要等待5秒钟。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> main(String[] args) </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ExecutorService exec </span><span style="color: #000000; ">=</span><span style="color: #000000; "> Executors.newFixedThreadPool(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ReentrantLock lock </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> ReentrantLock();<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> Condition con </span><span style="color: #000000; ">=</span><span style="color: #000000; "> lock.newCondition();<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> time </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">5</span><span style="color: #000000; ">;<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> Runnable add </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> Runnable() {<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> run() {<br />
<br />
System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Pre </span><span style="color: #000000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">+</span><span style="color: #000000; "> lock);<br />
<br />
lock.lock();<br />
<br />
</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "> {<br />
<br />
con.await(time, TimeUnit.SECONDS);<br />
<br />
} </span><span style="color: #0000FF; ">catch</span><span style="color: #000000; "> (InterruptedException e) {<br />
<br />
e.printStackTrace();<br />
<br />
} </span><span style="color: #0000FF; ">finally</span><span style="color: #000000; "> {<br />
<br />
System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Post </span><span style="color: #000000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">+</span><span style="color: #000000; "> lock.toString());<br />
<br />
lock.unlock();<br />
<br />
}<br />
<br />
}<br />
<br />
};<br />
<br />
</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> index </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">; index </span><span style="color: #000000; ">&lt;</span><span style="color: #000000; "> </span><span style="color: #000000; ">4</span><span style="color: #000000; ">; index</span><span style="color: #000000; ">++</span><span style="color: #000000; ">)<br />
<br />
exec.submit(add);<br />
<br />
exec.shutdown();<br />
<br />
}<br />
</span></div>
<p>&nbsp;</p>
<p><span  style="font-family: Tahoma; font-size: 12px; line-height: 18px; ">
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">这是它的输出：<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-2]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-3]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-4]</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">每一个线程的锁状态都是&#8220;Unlocked&#8221;,所以都可以运行。但在把con.await改成Thread.sleep(5000)时，输出就变成了：<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-2]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-3]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-4]<br />
</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">以上的对比说明线程在等待时(con.await)，已经不在拥有（keep）该锁了，所以其他线程就可以获得重入锁了。<br />
</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">有必要会过头再看看Java官方的解释：&#8220;如果该锁定被另一个线程保持，则出于线程调度的目的，禁用当前线程，并且在获得锁定之前，该线程将一直处于休眠状态&#8221;。我对这里的&#8220;保持&#8221;的理解是指非wait状态外的所有状态，比如线程Sleep、for循环等一切有CPU参与的活动。一旦线程进入wait状态后，它就不再keep这个锁了，其他线程就可以获得该锁；当该线程被唤醒（触发信号或者timeout）后，就接着执行，会重新&#8220;保持&#8221;锁，当然前提依然是其他线程已经不再&#8220;保持&#8221;了该重入锁。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">总结一句话：对于重入锁而言，"lock"和"keep"是两个不同的概念。lock了锁，不一定keep锁，但keep了锁一定已经lock了锁。</p>
</span></p>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/232227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-03 17:55 <a href="http://www.blogjava.net/rainmanyang/articles/232227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 理论与实践: 流行的原子</title><link>http://www.blogjava.net/rainmanyang/articles/232204.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Fri, 03 Oct 2008 06:35:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232204.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232204.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232204.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232204.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232204.html</trackback:ping><description><![CDATA[<span style="font-family: verdana; font-size: 12px; line-height: 19px; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">十五年前，多处理器系统是高度专用系统，要花费数十万美元（大多数具有两个到四个处理器）。现在，多处理器系统很便宜，而且数量很多，几乎每个主要微处理器都内置了多处理支持，其中许多系统支持数十个或数百个处理器。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">要使用多处理器系统的功能，通常需要使用多线程构造应用程序。但是正如任何编写并发应用程序的人可以告诉你的那样，要获得好的硬件利用率，只是简单地在多个线程中分割工作是不够的，还必须确保线程确实大部分时间都在工作，而不是在等待更多的工作，或等待锁定共享数据结构。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">问题：线程之间的协调</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果线程之间 <em>不</em>需要协调，那么几乎没有任务可以真正地并行。以线程池为例，其中执行的任务通常相互独立。如果线程池利用公共工作队列，则从工作队列中删除元素或向工作队列添加元素的过程必须是线程安全的，并且这意味着要协调对头、尾或节点间链接指针所进行的访问。正是这种协调导致了所有问题。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">标准方法：锁定</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">在 Java 语言中，协调对共享字段的访问的传统方法是使用同步，确保完成对共享字段的所有访问，同时具有适当的锁定。通过同步，可以确定（假设类编写正确）具有保护一组给定变量的锁定的所有线程都将拥有对这些变量的独占访问权，并且以后其他线程获得该锁定时，将可以看到对这些变量进行的更改。弊端是如果锁定竞争太厉害（线程常常在其他线程具有锁定时要求获得该锁定），会损害吞吐量，因为竞争的同步非常昂贵。（Public Service Announcement：对于现代 JVM 而言，无竞争的同步现在非常便宜。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">基于锁定的算法的另一个问题是：如果延迟具有锁定的线程（因为页面错误、计划延迟或其他意料之外的延迟），则 <em>没有</em>要求获得该锁定的线程可以继续运行。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">还可以使用可变变量来以比同步更低的成本存储共享变量，但它们有局限性。虽然可以保证其他变量可以立即看到对可变变量的写入，但无法呈现原子操作的读-修改-写顺序，这意味着（比如说）可变变量无法用来可靠地实现互斥（互斥锁定）或计数器。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">使用锁定实现计数器和互斥</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">假如开发线程安全的计数器类，那么这将暴露 <code>get()</code>、 <code>increment()</code> 和 <code>decrement()</code> 操作。清单 1 显示了如何使用锁定（同步）实现该类的例子。注意所有方法，甚至需要同步 <code>get()</code>，使类成为线程安全的类，从而确保没有任何更新信息丢失，所有线程都看到计数器的最新值。</p>
<br />
<a name="listing1"><strong>清单 1. 同步的计数器类</strong></a></span>
<font face="verdana" size="3"><span style="font-size: 12px; line-height: 19px; "><strong>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SynchronizedCounter {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> value;<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value; }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> increment() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #000000; ">++</span><span style="color: #000000; ">value; }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> decrement() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #000000; ">--</span><span style="color: #000000; ">value; }<br />
}</span></div>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><font face=" font-weight: normal; ">
</font></p>
<font face=" font-weight: normal; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><code>increment()</code> 和 <code>decrement()</code> 操作是原子的读-修改-写操作，为了安全实现计数器，必须使用当前值，并为其添加一个值，或写出新值，所有这些均视为一项操作，其他线程不能打断它。否则，如果两个线程试图同时执行增加，操作的不幸交叉将导致计数器只被实现了一次，而不是被实现两次。（注意，通过使值实例变量成为可变变量并不能可靠地完成这项操作。）</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">许多并发算法中都显示了原子的读-修改-写组合。清单 2 中的代码实现了简单的互斥， <code>acquire()</code> 方法也是原子的读-修改-写操作。要获得互斥，必须确保没有其他人具有该互斥（ <code>curOwner = Thread.currentThread()</code>），然后记录您拥有该互斥的事实（ <code>curOwner = Thread.currentThread()</code>），所有这些使其他线程不可能在中间出现以及修改 <code>curOwner field</code>。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing2"><strong>清单 2. 同步的互斥类</strong></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SynchronizedMutex {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> Thread curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> acquire() </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (Thread.interrupted()) </span><span style="color: #0000FF; ">throw</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> InterruptedException();<br />
</span><span style="color: #0000FF; ">while</span><span style="color: #000000; "> (curOwner </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">) <br />
wait();<br />
curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> Thread.currentThread();<br />
}<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> release() {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (curOwner </span><span style="color: #000000; ">==</span><span style="color: #000000; "> Thread.currentThread()) {<br />
curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
notify();<br />
} </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />
</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> IllegalStateException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">not owner of mutex</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
}<br />
}</span></div>
</strong></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">清单 1 中的计数器类可以可靠地工作，在竞争很小或没有竞争时都可以很好地执行。然而，在竞争激烈时，这将大大损害性能，因为 JVM 用了更多的时间来调度线程，管理竞争和等待线程队列，而实际工作（如增加计数器）的时间却很少。您可以回想 </span><a href="http://www.ibm.com/developerworks/java/library/j-jtp10264/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" style="color: #5c81a7; "><span style="font-weight: normal;">上月专栏</span></a><span style="font-weight: normal;">中的图，该图显示了一旦多个线程使用同步竞争一个内置监视器，吞吐量将如何大幅度下降。虽然该专栏说明了新的 </span><code><span style="font-weight: normal;">ReentrantLock</span></code><span style="font-weight: normal;"> 类如何可以更可伸缩地替代同步，但是对于一些问题，还有更好的解决方法。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.2"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-size: 15px; "><span style="font-weight: normal;">锁定问题</span></span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">使用锁定，如果一个线程试图获取其他线程已经具有的锁定，那么该线程将被阻塞，直到该锁定可用。此方法具有一些明显的缺点，其中包括当线程被阻塞来等待锁定时，它无法进行其他任何操作。如果阻塞的线程是高优先级的任务，那么该方案可能造成非常不好的结果（称为 </span><span style="font-weight: normal;">优先级倒置</span><span style="font-weight: normal;">的危险）。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">使用锁定还有一些其他危险，如死锁（当以不一致的顺序获得多个锁定时会发生死锁）。甚至没有这种危险，锁定也仅是相对的粗粒度协调机制，同样非常适合管理简单操作，如增加计数器或更新互斥拥有者。如果有更细粒度的机制来可靠管理对单独变量的并发更新，则会更好一些；在大多数现代处理器都有这种机制。</span></p>
<strong><br />
</strong></font></strong></span><strong><font face=" font-weight: normal; "></font>
<p>&#160;</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><font face=" font-weight: normal; "><strong><br />
</strong>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
</font></p>
<font face=" font-weight: normal; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.0"></a></p>
<span style="font-size: 10pt; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.0"><span class="atitle" style="font-family: Arial, sans-serif; font-size: 18px; "><span style="font-weight: normal;">硬件同步原语</span></span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">如前所述，大多数现代处理器都包含对多处理的支持。当然这种支持包括多处理器可以共享外部设备和主内存，同时它通常还包括对指令系统的增加来支持多处理的特殊要求。特别是，几乎每个现代处理器都有通过可以检测或阻止其他处理器的并发访问的方式来更新共享变量的指令。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-size: 15px; "><span style="font-weight: normal;">比较并交换 (CAS)</span></span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">支持并发的第一个处理器提供原子的测试并设置操作，通常在单位上运行这项操作。现在的处理器（包括 Intel 和 Sparc 处理器）使用的最通用的方法是实现名为 </span><em><span style="font-weight: normal;">比较并转换</span></em><span style="font-weight: normal;">或 CAS 的原语。（在 Intel 处理器中，比较并交换通过指令的 cmpxchg 系列实现。PowerPC 处理器有一对名为&#8220;加载并保留&#8221;和&#8220;条件存储&#8221;的指令，它们实现相同的目地；MIPS 与 PowerPC 处理器相似，除了第一个指令称为&#8220;加载链接&#8221;。）</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">CAS 操作包含三个操作数 —— 内存位置（V）、预期原值（A）和新值(B)。如果内存位置的值与预期原值相匹配，那么处理器会自动将该位置值更新为新值。否则，处理器不做任何操作。无论哪种情况，它都会在 CAS 指令之前返回该位置的值。（在 CAS 的一些特殊情况下将仅返回 CAS 是否成功，而不提取当前值。）CAS 有效地说明了&#8220;我认为位置 V 应该包含值 A；如果包含该值，则将 B 放到这个位置；否则，不要更改该位置，只告诉我这个位置现在的值即可。&#8221;</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">通常将 CAS 用于同步的方式是从地址 V 读取值 A，执行多步计算来获得新值 B，然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改，则 CAS 操作成功。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">类似于 CAS 的指令允许算法执行读-修改-写操作，而无需害怕其他线程同时修改变量，因为如果其他线程修改变量，那么 CAS 会检测它（并失败），算法可以对该操作重新计算。清单 3 说明了 CAS 操作的行为（而不是性能特征），但是 CAS 的价值是它可以在硬件中实现，并且是极轻量级的（在大多数处理器中）：</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing3"><span style="font-weight: normal;">清单 3. 说明比较并交换的行为（而不是性能）的代码</span></a></p>
</span>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing3"><strong></strong></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SimulatedCAS {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> value;<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value; }<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> compareAndSwap(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> expectedValue, </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> newValue) {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (value </span><span style="color: #000000; ">==</span><span style="color: #000000; "> expectedValue) <br />
value </span><span style="color: #000000; ">=</span><span style="color: #000000; "> newValue;<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value;<br />
}<br />
}</span></div>
</strong></p>
</font>
<p><span style="font-size: 12px; font-weight: normal; line-height: 19px; ">
</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.2"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">用 CAS 实现计数器</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">基于 CAS 的并发算法称为 <em>无锁定</em>算法，因为线程不必再等待锁定（有时称为互斥或关键部分，这取决于线程平台的术语）。无论 CAS 操作成功还是失败，在任何一种情况中，它都在可预知的时间内完成。如果 CAS 失败，调用者可以重试 CAS 操作或采取其他适合的操作。清单 4 显示了重新编写的计数器类来使用 CAS 替代锁定：</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> CasCounter {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> SimulatedCAS value;<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() {<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value.getValue();<br />
}<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> increment() {<br />
</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> oldValue </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value.getValue();<br />
</span><span style="color: #0000FF; ">while</span><span style="color: #000000; "> (value.compareAndSwap(oldValue, oldValue </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">) </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> oldValue)<br />
oldValue </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value.getValue();<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> oldValue </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br />
}<br />
}</span></div>
<p>&nbsp;</p>
<p><span  style="font-size: 12px; font-weight: normal; line-height: 19px; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">无锁定且无等待算法</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果每个线程在其他线程任意延迟（或甚至失败）时都将持续进行操作，就可以说该算法是&#160;<em>无等待</em>的。与此形成对比的是，&#160;<em>无锁定</em>算法要求仅&#160;<em>某个</em>线程总是执行操作。（无等待的另一种定义是保证每个线程在其有限的步骤中正确计算自己的操作，而不管其他线程的操作、计时、交叉或速度。这一限制可以是系统中线程数的函数；例如，如果有 10 个线程，每个线程都执行一次&#160;<code>CasCounter.increment()</code>&#160;操作，最坏的情况下，每个线程将必须重试最多九次，才能完成增加。）</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">再过去的 15 年里，人们已经对无等待且无锁定算法（也称为&#160;<em>无阻塞算法</em>）进行了大量研究，许多人通用数据结构已经发现了无阻塞算法。无阻塞算法被广泛用于操作系统和 JVM 级别，进行诸如线程和进程调度等任务。虽然它们的实现比较复杂，但相对于基于锁定的备选算法，它们有许多优点：可以避免优先级倒置和死锁等危险，竞争比较便宜，协调发生在更细的粒度级别，允许更高程度的并行机制等等。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">原子变量类</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">在 JDK 5.0 之前，如果不使用本机代码，就不能用 Java 语言编写无等待、无锁定的算法。在&#160;<code>java.util.concurrent.atomic</code>&#160;包中添加原子变量类之后，这种情况才发生了改变。所有原子变量类都公开比较并设置原语（与比较并交换类似），这些原语都是使用平台上可用的最快本机结构（比较并交换、加载链接/条件存储，最坏的情况下是旋转锁）来实现的。&#160;<code>java.util.concurrent.atomic</code>&#160;包中提供了原子变量的 9 种风格（&#160;<code>AtomicInteger</code>；&#160;<code>AtomicLong</code>；&#160;<code>AtomicReference</code>；&#160;<code>AtomicBoolean</code>；原子整型；长型；引用；及原子标记引用和戳记引用类的数组形式，其原子地更新一对值）。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">原子变量类可以认为是&#160;<code>volatile</code>&#160;变量的泛化，它扩展了可变变量的概念，来支持原子条件的比较并设置更新。读取和写入原子变量与读取和写入对可变变量的访问具有相同的存取语义。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">虽然原子变量类表面看起来与清单 1 中的&#160;<code>SynchronizedCounter</code>&#160;例子一样，但相似仅是表面的。在表面之下，原子变量的操作会变为平台提供的用于并发访问的硬件原语，比如比较并交换。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.2"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">更细粒度意味着更轻量级</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">调整具有竞争的并发应用程序的可伸缩性的通用技术是降低使用的锁定对象的粒度，希望更多的锁定请求从竞争变为不竞争。从锁定转换为原子变量可以获得相同的结果，通过切换为更细粒度的协调机制，竞争的操作就更少，从而提高了吞吐量。</p>
<table align="right" border="0" cellspacing="0" cellpadding="0" width="40%">
    <tbody>
        <tr>
            <td width="10" style="line-height: 19px; "><img alt="" height="1" width="10" src="http://www.ibm.com/i/c.gif" /></td>
            <td style="line-height: 19px; ">
            <table border="1" cellspacing="0" cellpadding="5" width="100%">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee" style="line-height: 19px; "><a name="sidebar1"><strong>ABA 问题</strong></a><br />
                        因为在更改 V 之前，CAS 主要询问&#8220;V 的值是否仍为 A&#8221;，所以在第一次读取 V 以及对 V 执行 CAS 操作之前，如果将值从 A 改为 B，然后再改回 A，会使基于 CAS 的算法混乱。在这种情况下，CAS 操作会成功，但是在一些情况下，结果可能不是您所预期的。（注意，&#160;<a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#listing1" style="color: #5c81a7; ">清单 1</a>&#160;和&#160;<a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#listing2" style="color: #5c81a7; ">清单 2</a>&#160;中的计数器和互斥例子不存在这个问题，但不是所有算法都这样。）这类问题称为&#160;<em>ABA 问题</em>，通常通过将标记或版本编号与要进行 CAS 操作的每个值相关联，并原子地更新值和标记，来处理这类问题。&#160;<code>AtomicStampedReference</code>&#160;类支持这种方法。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.3"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">java.util.concurrent 中的原子变量</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">无论是直接的还是间接的，几乎&#160;<code>java.util.concurrent</code>&#160;包中的所有类都使用原子变量，而不使用同步。类似<code>ConcurrentLinkedQueue</code>&#160;的类也使用原子变量直接实现无等待算法，而类似&#160;<code>ConcurrentHashMap</code>&#160;的类使用&#160;<code>ReentrantLock</code>&#160;在需要时进行锁定。然后，&#160;<code>ReentrantLock</code>&#160;使用原子变量来维护等待锁定的线程队列。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果没有 JDK 5.0 中的 JVM 改进，将无法构造这些类，这些改进暴露了（向类库，而不是用户类）接口来访问硬件级的同步原语。然后，java.util.concurrent 中的原子变量类和其他类向用户类公开这些功能。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="4.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">使用原子变量获得更高的吞吐量</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a href="http://www.ibm.com/developerworks/java/library/j-jtp10264/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" style="color: #5c81a7; ">上月</a>，我介绍了&#160;<code>ReentrantLock</code>&#160;如何相对于同步提供可伸缩性优势，以及构造通过伪随机数生成器模拟旋转骰子的简单、高竞争示例基准。我向您显示了通过同步、&#160;<code>ReentrantLock</code>&#160;和公平&#160;<code>ReentrantLock</code>&#160;来进行协调的实现，并显示了结果。本月，我将向该基准添加其他实现，使用&#160;<code>AtomicLong</code>&#160;更新 PRNG 状态的实现。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">清单 5 显示了使用同步的 PRNG 实现和使用 CAS 备选实现。注意，要在循环中执行 CAS，因为它可能会失败一次或多次才能获得成功，使用 CAS 的代码总是这样。</p>
<br />
<a name="listing5"><strong>清单 5. 使用同步和原子变量实现线程安全 PRNG</strong></a><br />
<table width="100%" cellpadding="0" cellspacing="0" border="0">
    <tbody>
        <tr>
            <td class="code-outline" style="line-height: 19px; background-color: #eeeeee; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; ">
            <pre class="displaycode" style="margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, fixed, monospace; font-size: 11px; ">
            public class PseudoRandomUsingSynch implements PseudoRandom {
            private int seed;
            public PseudoRandomUsingSynch(int s) { seed = s; }
            public synchronized int nextInt(int n) {
            int s = seed;
            seed = Util.calculateNext(seed);
            return s % n;
            }
            }
            public class PseudoRandomUsingAtomic implements PseudoRandom {
            private final AtomicInteger seed;
            public PseudoRandomUsingAtomic(int s) {
            seed = new AtomicInteger(s);
            }
            public int nextInt(int n) {
            for (;;) {
            int s = seed.get();
            int nexts = Util.calculateNext(s);
            if (seed.compareAndSet(s, nexts))
            return s % n;
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">下面图 1 和图 2 中的图与上月那些图相似，只是为基于原子的方法多添加了一行。这些图显示了在 8-way Ultrasparc3 和单处理器 Pentium 4 上使用不同数量线程的随机发生的吞吐量（以每秒转数为单位）。测试中的线程数不是真实的；这些线程所表现的竞争比通常多得多，所以它们以比实际程序中低得多的线程数显示了&#160;<code>ReentrantLock</code>&#160;与原子变量之间的平衡。您将看到，虽然&#160;<code>ReentrantLock</code>&#160;拥有比同步更多的优点，但相对于&#160;<code>ReentrantLock</code>，原子变量提供了其他改进。（因为在每个工作单元中完成的工作很少，所以下图可能无法完全地说明与 ReentrantLock 相比，原子变量具有哪些可伸缩性优点。）</p>
<br />
<a name="fig1"><strong>图 1. 8-way Ultrasparc3 中同步、ReentrantLock、公平 Lock 和 AtomicLong 的基准吞吐量</strong></a><br />
<img alt="8-way Ultrasparc3 吞吐量" height="332" src="http://www.ibm.com/developerworks/cn/java/j-jtp11234/RngThroughput.gif" width="550" />&#160;<br />
<br />
<a name="fig2"><strong>图 2. 单处理器 Pentium 4 中的同步、ReentrantLock、公平 Lock 和 AtomicLong 的基准吞吐量</strong></a><br />
<img alt="Uniprocessor Pentium4 吞吐量" height="328" src="http://www.ibm.com/developerworks/cn/java/j-jtp11234/RngThroughputUni.gif" width="551" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">大多数用户都不太可能使用原子变量自己开发无阻塞算法 — 他们更可能使用&#160;<code>java.util.concurrent</code>&#160;中提供的版本，如&#160;<code>ConcurrentLinkedQueue</code>。但是万一您想知道对比以前 JDK 中的相类似的功能，这些类的性能是如何改进的，可以使用通过原子变量类公开的细粒度、硬件级别的并发原语。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">开发人员可以直接将原子变量用作共享计数器、序号生成器和其他独立共享变量的高性能替代，否则必须通过同步保护这些变量。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="5.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">结束语</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">JDK 5.0 是开发高性能并发类的巨大进步。通过内部公开新的低级协调原语，和提供一组公共原子变量类，现在用 Java 语言开发无等待、无锁定算法首次变为可行。然后，&#160;<code>java.util.concurrent</code>&#160;中的类基于这些低级原子变量工具构建，为它们提供比以前执行相似功能的类更显著的可伸缩性优点。虽然您可能永远不会直接使用原子变量，还是应该为它们的存在而欢呼。</p>
</span></p>
</strong></font>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/232204.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-03 14:35 <a href="http://www.blogjava.net/rainmanyang/articles/232204.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>