<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>我的BT下载实验室</title>
    <description></description>
    <link>http://wangdei.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
          <item>
        <title>博客变味：从一方净土沦落为企业枪手（转）</title>
        <author>wangdei</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://wangdei.javaeye.com">wangdei</a>&nbsp;
                    链接：<a href="http://wangdei.javaeye.com/blog/246798" style="color:red;">http://wangdei.javaeye.com/blog/246798</a>&nbsp;
          发表时间: 2008年09月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <table cellspacing="0" border="0" align="center" width="100%" cellpadding="0">
<tbody>
<tr>
<td><span style="color: #333399;">编者按<br />&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; 天下熙熙，皆为利来；天下攘攘，皆为利往。曾被誉为<a href="http://www.bt285.cn">互联网</a>最后一块纯净的天空&mdash;&mdash;博客也抵挡不住金钱利益的诱惑，充满了铜臭。<br />&nbsp;&nbsp;&nbsp; 事实上，<a href="http://www.5a520.cn">博客经济</a>已形成了一条分工明确、组织严密的利益产业链。为达到目的，他们背离道德底线，不择手段地充当打手。<br />&nbsp;&nbsp;&nbsp; 一心只为钱财的博客，也许可以买来一时的满足和快意，但后果可能是博客诚信荡然无存，摧毁了整个博客业态。 <br />&nbsp;&nbsp;&nbsp; 变质的博客，还会有人留恋吗？<br />&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; 博客，原本是人们在互联网上抒发个人情感、交流思想、表情达意的一方净土。如今，很多博客却已经开始变味，直接沦为企业的枪手，博主们也频频出入于企业的新闻发布会，借助自己的博客平台为企业摇旗呐喊。<br />&nbsp;&nbsp;&nbsp; 一年前，26岁的陈振 (化名）只是从重庆来京的&ldquo;北漂&rdquo;一员，现在他成了IT博客圈里&ldquo;有固定收入&rdquo;的人。每个月他可从公关那里获得500-1500元的&ldquo;稿费&rdquo;，最高时能达4000元以上。<br />&nbsp;&nbsp;&nbsp; 陈振并非通过写博赚钱的个例。&ldquo;其实，像他这样的草根帮企业写博赚的钱很少，IT博客界大腕或者顶级枪手，一篇千字的文章就能开到5000元以上的价位。&rdquo;一位博客撰稿人对《IT时代周刊》透露，少数博主为了捞钱，甚至在博客中公然发布虚假软文，或替东家攻击竞争对手，彻底沦为商家的御用文人。<br />&nbsp;&nbsp;&nbsp; 真实、有趣、无拘束的博客，如何逐渐变了味道？ </span></td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr>
<td align="center">
<table cellspacing="1" border="0" align="center" bgcolor="#f5f5f5" cellpadding="0">
<tbody>
<tr>
<td bgcolor="#ffffff">
<table cellspacing="1" border="0" bgcolor="#ebebeb" width="100%" cellpadding="0">
<tbody>
<tr>
<td bgcolor="#ffffff">
<table cellspacing="1" border="0" bgcolor="#e1e1e1" width="100%" cellpadding="0">
<tbody>
<tr>
<td>
<table cellspacing="1" border="0" bgcolor="#c8c8c8" cellpadding="3">
<tbody>
<tr>
<td align="center" bgcolor="#ffffff"><img src="http://wangdei.javaeye.com/admin/blogs/cover/160.jpg" border="0" align="center" alt="" /> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td id="content"><span class="content2">
<p><strong><span style="color: #0000ff;"><span style="color: #ff0000;">第一章</span>&nbsp; 博客蜕变</span></strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 2008年，有人说，如果你现在还未涉足博客，那么&ldquo;你是从火星来的&rdquo;。越来越多的博主成为公关公司和厂商们的座上宾，博客在商业利益的&ldquo;糖衣炮弹&rdquo;中开始步步沦落。</p>
<br />
<p><strong>变身</strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 2000年，博客出现在中国互联网上，迅速吸引网民的兴趣。看到机遇的方兴东，在2002年创立&ldquo;博客中国网&rdquo;（后更名为博客网）。虽然此后博客网因经营不善而逐渐被边缘化，但博客却在门户网站的大力推广下得到蓬勃发展，开创了一个&ldquo;全民开博&rdquo;的时代。一时，名人博客和草根博客成为业界主流。<br />&nbsp;&nbsp;&nbsp; 对于一件产品，消费者愿意相信商家广告，还是口口相传、用户现身说法的博客?相信绝大多数人会选择后者。相对于其他行业而言，IT企业更是对博客营销情有独钟。<br />&nbsp;&nbsp;&nbsp; 2005年8月，IBM发布了专门针对投资人的博客站点，讨论商业和技术主题，如银行业、购物和网络游戏等。2007年，戴尔、谷歌先后开通中文博客，介绍各自产品、技术和文化。目前，微软也开展了不计流量和知名度的博客推广合作。<br />&nbsp;&nbsp;&nbsp; 在一片名人开博的形势下，众多CEO也陆续开博，为各自企业摇旗呐喊。其中的代表是英特尔和微软中国公司。前者号召自己的总裁和副总裁写博，英特尔公司副总裁、英特尔中国大区总经理杨叙就曾多次将博文群发到客户邮箱；在后者的博客中，微软全球资深副总裁、微软中国公司董事长张亚勤也频频露脸。<br />&nbsp;&nbsp;&nbsp; 于是，原本以交流观点为主的博客开始慢慢地演变成众商家对外宣传的平台。2007年6月15日，搜狐公司董事长张朝阳在自己博客中贴出一篇题为《竞争手段》的博文，并推至搜狐博客首页，成为公开挑战新浪的&ldquo;宣战书&rdquo;。而在之后搜狐与谷歌的输入法纠纷中，谷歌中文官方博客&mdash;&mdash;谷歌黑板报，则成为其对外表态的新窗口。就在不久前，谷歌在全球100多个国家力推Chrome浏览器的重磅消息竟源于其在官方博客中的声明。<br />&nbsp;&nbsp;&nbsp; 博客之所以能成为商家的宣传阵地，还能博得众人喜爱，原因是它能用最通俗的语言将企业的诉求有效传达给消费者，使得新品迅速扩大知名度，成为网络讨论的焦点。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有需求便有市场，于是在IT领域内，开始慢慢形成了以企业CEO高管个人开博、企业集体以品牌开博、普通网民以分析评论开博3大类别。而普通网民的博客由于贴近消费者，开始成为公关公司和商家们从事市场推广的新宠。<br />&nbsp;&nbsp;&nbsp; 2007年11月的一天，在北京大望路附近的一家餐厅内，中国雅虎的8位高管与IT界十多位知名博主就&ldquo;雅虎搜索&rdquo;展开讨论，&ldquo;搜索一姐&rdquo;张忆芬和时任雅虎公关经理的徐杨均出席了会议。会议从下午2点一直持续到晚上8点多，而博主们提出的&ldquo;搜索引擎中收录商品商标&rdquo;的建议当场被采纳。<br />&nbsp;&nbsp;&nbsp; &ldquo;起初，博主关注的话题与企业宣传方向一致时，大家见面吃吃饭聊聊天，聊的内容和感受，博主都会写成文章，无需花费另外的费用。&rdquo;一公关公司的职员告诉本刊记者，&ldquo;现在博主都很&lsquo;现实&rsquo;了，简单吃饭行不通，真金白银才能撬动他们手中的笔。&rdquo; </p>
<br />
<p><strong>月收入过万</strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 2008年年初，房地产界的风云人物、SOHO老板潘石屹在新浪博客表现出众，他日日有新作，而且文章观点新颖，点击率半年已超百万。但据未经证实的消息称，潘石屹专门聘请了四五个精通房产的专业写手，为其效力。<br />&nbsp;&nbsp;&nbsp; 在博客日益受宠的背后，一些博主开始匿名扮演企业吹鼓手的角色，帮助商家大打口水战、博文中带有企业公关的痕迹已比比皆是。发展至今，一些公司已在暗地里用金钱和实物直接收买第三方博客，甚至出资建非品牌的博客网站，为自己的产品做促销或打压竞争对手。<br />&nbsp;&nbsp;&nbsp; 据消息人士透露，在最近沸沸扬扬的一场口水战中，杀毒领域的瑞星公司仅在博客软文上的投入就超过20万元。舆论导向曾一度偏向自己，幕后的博客发挥了不可小觑的作用。而此前，手机厂商巨头诺基亚也曾投入资金在博客网上建手机博客组群，其一度成为网民购买其手机的主要参考对象。<br />&nbsp;&nbsp;&nbsp; 博客交易的不断成熟和良好效果刺激了越来越多的国内企业悄然试水，并逐步向娱乐、房产、化妆品等领域渗透，甚至开始触及传统行业。征文大赛、选美、选代言人、宣传公司产品和品牌、攻击对手等都可利用博客，与广告和其他营销相比，博客可提供附加值，以小投入获得大的宣传回报，是一条直达受众的理想渠道。这也是博客被收买的前提。<br />&nbsp;&nbsp;&nbsp; 在一切都向商业靠拢的大环境下，博客交易逐渐扩散。一些被称之为&ldquo;流氓打手&rdquo;的博客写手则接受企业钱财，撰文为其消灾。<br />&nbsp;&nbsp;&nbsp; 坊间广为流传的&ldquo;打手公司&rdquo;&mdash;&mdash;&ldquo;1024&rdquo;就曾参与利用博客攻击竞争对手的产品及形象策划。据报道，曾被分众传媒以1000万美元收购的&ldquo;1024&rdquo;公司因极具攻击性而被指责为&ldquo;网络打手&rdquo;，其甚至一度引起法律纠纷。而在过去发生在众杀毒厂商之间的多起口水战中，其就通过博客、论坛等频频出手。<br />&nbsp;&nbsp;&nbsp; &ldquo;1024&rdquo;首席执行官童紫静甚至曾公开宣称：&ldquo;1024拥有1000人的营销队伍，可监控10万个论坛，并掌控了独到的网络言论监控系统，可在第一时间为企业提供网络言论的监控报告，并对相关言论作出迅速相应反应。按照坊间广为流传的说法，&ldquo;1024&rdquo;还备有互有分工的论坛灌水人员，随时待命出击。<br />&nbsp;&nbsp;&nbsp; 就这样，原本代表&ldquo;客观民意&rdquo;的博客天平开始日渐倾斜。<br />&nbsp;&nbsp;&nbsp; 据了解，帮企业写话题软文，利用个人博客品牌发布公关稿，或在博客中插入图片帮企业做推广，这已经成为众多博客的生财之道。所谓话题软文，即博客作者通过在自己博客上为商家产品写评论，向读者介绍产品，以达到传播目的。<br />&nbsp;&nbsp;&nbsp; 根据知名度和流量的不同，软文费用差别很大。在IT博客领域，千字稿费在500-2000元，普通博主分文难取，知名博主为500-700元一篇，顶级博客一篇就能达到5000元，一些被企业指定的博文的润笔费用则更高，月收入过万已并不鲜见。</p>
<br />
<p><strong><span style="color: #0000ff;"><span style="color: #ff0000;">第二章&nbsp;</span> 利益链</span></strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 互联网经济的发达及广告商的渗入，使得有偿写博的阶层逐步产生并扩大，从而彻底打破了博客真实、公正这一&ldquo;潜规则&rdquo;。据不完全统计，目前这个群体在全球的数量已超十万。<br />&nbsp;&nbsp;&nbsp; 在国内，随着越来越多的广告商对个人博客流量给予的经济回报，许多网民从博客的流量中找到了挣钱新渠道，成为博客软文的职业者，而&ldquo;枪手&rdquo;也开始多了起来。 <br />&nbsp;&nbsp;&nbsp; 在经济利益的大棒下，一些博客开始&ldquo;变味&rdquo;。为了吸引眼球、增加流量，以获得更多收益，编造故事、揭露他人隐私、恶意诽谤都成为其惯用的手段。以博客作为交易工具的利益链条逐渐成型。 </p>
<br />
<p><strong>拜金主义盛行</strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; &ldquo;在这里，您及贵公司只需发布一个&lsquo;命题征文&rsquo;，藏龙卧虎的博客作者将按照您的要求撰写博文。您只需付出少量费用，就可通过博文的口碑宣传您的产品，使您再也不用去求报社&hellip;&hellip;&rdquo;这是近期一家网站推出的&ldquo;博客赚钱&rdquo;的专题导语。<br />&nbsp;&nbsp;&nbsp; 据悉，企业只需在这个网上通过注册、给出命题、预付定金，剩下的由专门的操作公司来完成整个博客交易。<br />&nbsp;&nbsp;&nbsp; &ldquo;若网友按主题及要求撰写合格博客，我们将为您提供每篇千字25元-200元甚至更高的酬金&rdquo;，博客操作公司一边蛊惑企业做博客公关，一边向博主们&ldquo;广而告之&rdquo;。为了收到实效，网站上一并附上了软文欣赏、软文写手群的链接及联系人的邮箱、QQ等。<br />&nbsp;&nbsp;&nbsp; 上述现象只是通过网站宣传进行博客交易方式的其中一种。对于更多的大企业来说，直接派公关或市场部人员跟博客操作公司接洽是最常用的手法。在博客大行其道的2008年，博客交易已成燎原之势。一公关公司员工掌握的数据是，有近1/3的国际公关公司都在涉水博客公关。他认为，虽然国际公关公司都很&ldquo;爱惜羽毛&rdquo;，但不排除其中个别有幕后交易情形。<br />&nbsp;&nbsp;&nbsp; 实际上，早在2006年6月，博客网在方兴东的统帅下，就以&ldquo;美女博客&rdquo;的形式首次将博客交易&mdash;&mdash;软文赢利模式推到数万网民的面前。<br />&nbsp;&nbsp;&nbsp; 当时，博客网通过用户填写的详细资料，精心挑选出500位美女的博客，并根据厂商提供的产品将美女博主细分，以产生间接促进软性广告的个人博客渠道。博主们可获得该品牌公司每月提供的价值200元的产品。<br />&nbsp;&nbsp;&nbsp; 当时，按照博客网的规划，这些女博客在自己的文章里大谈产品使用感受，和博主价值观相同的粉丝们，通过博客上的广告条点击购买折扣券。众多美女博客作者就这样演变成某些化妆品牌的&ldquo;代言人&rdquo;。<br />&nbsp;&nbsp;&nbsp; 但在博客还未成熟之际，博客难以带来高点击率，形成规模效应，大多数个人博客的影响力仅限于博客作者各自的交际圈，很难有所扩大。此外，那时许多普通的博客作者并不太关注通过自己的博客来谋取经济利益。在这种情况下，博客软文推广价值不高。<br />&nbsp;&nbsp;&nbsp; 目前，中国网民数已经超过2.5亿，各门户博客频道的点击率也不断攀升，据CNNIC报告，截止到2007年11月底，中国博客空间已达7282万个，博客作者人数达4700万，平均近每4个网民中就有一个博客作者。就在2007年，某门户网站有一个博客，竟然在短短的3个月的时间内点击率就超过1000万人次，其中有一天的点击率就达到46万人次。<br />&nbsp;&nbsp;&nbsp; 业内专家撰文预测，当博客点击率高到足以出现一呼百应的程度，博客软文推广方面的价值将再度催生。而据市场咨询机构统计，仅从2007年12月到2008年6月，新浪加盟博主数量在原基础上增加了3倍，带入站内个人博客超过46%的流量。而在2008年，本刊记者也不断接到企业和公关公司的&ldquo;求助&rdquo;电话，希望借媒体的资源帮助对方物色知名博客写手。</p>
<br />
<p><strong>交易一条龙</strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 那么，具体博客交易到底是通过怎样的流程来进行的？本刊记者辗转找到了一位曾经专门操作博客交易的某公司前员工刘某，她讲述了其中的奥秘。<br />&nbsp;&nbsp;&nbsp; 整个利益链条由一些咨询公司在幕后专门操纵。&ldquo;从提案到商讨、找博主实施和反馈分析，经过厂商、公关公司、博主、博客营销公司和门户博客网站这五关，构建成一条完整的利益互惠产业链&rdquo;。<br />&nbsp;&nbsp;&nbsp; 对于众多博主而言，要通过博客赚钱，增加流量、提升名气是首要一关。获得足够流量（一般全部流量加起来达到百万级）和有名气的博客会被门户博客网站管理中心或专门的博客营销公司&ldquo;记录在案&rdquo;。公关公司根据企业推广需求向商家提供博客公关提案，厂家确认后，再由公关公司联系一些影响较大的门户博客管理中心，再找到相应的博主进行撰文，最后审核后出文、付费、反馈，形成交易一条龙。<br />&nbsp;&nbsp;&nbsp; 实际上，公关公司或企业市场部首次与博主联系时，主要通过门户博客管理中心，由管理中心邀请该博主参加新闻发布会。在发布会现场，公关人员认识博主后，会给博主200-300元的&ldquo;车马费&rdquo;，这算是双方的第一次正式接洽。<br />&nbsp;&nbsp;&nbsp; 摆平了博主只是整个计划的开始，要&ldquo;拿下&rdquo;门户博客管理中心，工作才算告一段落。有知情的公关公司工作人员就透露，&ldquo;通过博客网站管理中心找博主，我们需要付给门户网站上万元的费用，第二次跟博主合作就可省去这一步骤，直接联系博主，说明来意和价格。双方谈判成功后，我们会对稿件提出具体要求，整个环节通常用电话、邮件、QQ和MSN进行。&rdquo;<br />&nbsp;&nbsp;&nbsp; &ldquo;这样做的目的，无非是让博客网站对博主撰写的公关稿大开方便之门，使之能够被置顶，呈现在比较醒目的位置，以达到宣传效果。&rdquo;该工作人员说。<br />&nbsp;&nbsp;&nbsp; 另据《IT时代周刊》了解，在此期间，负责博客公关的营销机构还有一些细致的工作要做，他们不仅要对知名博主的联系方式进行录入整理，而且对博主的文笔、博客流量、被网站推荐频次都有分析，甚至每个博主最擅长的笔法和名气排行都有详细记录。作为中间人的策划机构，则按策划活动的总时间向企业收费，而幕后操纵的营销公司年收入能达到千万元级别。<br />&nbsp;&nbsp;&nbsp; 一公关公司的总监透露，一般直接联系博主进行博客公关的费用，博主能获取30%，公关公司获得50%-60%。若要将博文推广到类似新浪、搜狐等顶级博客网站的首页，还需向媒体博客的编辑支付一定的推荐费。据搜狐博客一员工透露，&ldquo;如果一篇软文需放到博客频道首页，则需走销售部，按广告进行收费。&rdquo;<br />&nbsp;&nbsp;&nbsp; 对于整个博客交易，有商家算了一笔明细账：一次博客营销全部花费以10篇博文来计算：向博主支付0.5万-1万元，公关公司获得近2万元收入，加上&ldquo;进贡&rdquo;给编辑的辛苦费，共计3万元左右，而企业举行小型的发布会也得花费4万-5万元。小投入赢得好的宣传效果，又很省心，何乐而不为？</p>
<br />
<p><strong><span style="color: #0000ff;"><span style="color: #ff0000;">第三章&nbsp;</span> 双刃剑</span></strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; &ldquo;花非花，雾非雾，博客公关无重数&rdquo;。对于企业而言，博客公关并没有任何成功版本可言，其无异于一把双刃剑，企业在具体操作中必须具体甄别，切莫反被刺伤。&nbsp;&nbsp;&nbsp; </p>
<br />
<p><strong>品苦果</strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 各大公司都在不同程度上涉足博客交易，而博客被企业收买的幕后，水很深、很浑。一些博主为了钱，能把黑说成白。欧洲部分公司甚至出现了专门的公司营销部门，建立起大量伪造的博客，以特定的语言向消费者介绍相关的产品，严重扰乱消费者的视线。<br />&nbsp;&nbsp;&nbsp; 无独有偶，就在2006年12月21日，国内IT社区Donews撰写的《美国：企业赞助Blogger宣传产品涉嫌违法》一文称，美国PayPerPost公司收取一些公司的广告赞助，然后支付一部分金额让博主写产品宣传稿子而遭到联邦公平委员会指责：&ldquo;这种行为违反消费者保护权益，涉嫌欺诈。&rdquo;<br />&nbsp;&nbsp;&nbsp; 也许它目前并不违法，但这却无疑给外界一个信号：欧美终将开始整治这一不正当行为。届时，各种以软文、伪造用户口碑宣传的商业博客将通过立法被解决掉，而对假扮消费者行为和在一些消费者评级网站上作弊的公司则将以刑事或民事赔偿责任论处。<br />&nbsp;&nbsp;&nbsp; 对于国内博客软文等交易，虽然许多人士认为这很正常，是一种社会化营销。但 &ldquo;厂商枪手已大量出现，博客一旦发展为图谋钱财的工具，将可能背离道德底线，摧毁整个博客行业&rdquo;。一国内业内人士表示，&ldquo;用金钱收买而来的博客，也许可以一时辉煌和灿烂，但在不久的将来必然荡然无存，徒留下一片凄凉。&rdquo; <br />&nbsp;&nbsp;&nbsp; &ldquo;博主们总以自己的文字自居，沾沾自喜地认为企业找他们写博客枪稿，自己就有身价。但如果为了金钱而说有失公平的话，就失去了写博客日志的意义和市场。&rdquo;网民们对博客被企业的收买现象如此表示。<br />&nbsp;&nbsp;&nbsp; 专家强调，对一款产品做博客软文宣传时，博主除了应对其优势进行&ldquo;吹捧&rdquo;，也应对其不足进行&ldquo;善意提醒&rdquo;，这样才能以其公正赢得读者共鸣，才更具推广价值。<br />&nbsp;&nbsp;&nbsp; 对企业来说，博客交易真能如企业所愿，成为传播品牌和拉动产品销量的制胜法宝吗？对此，专家们发出了不同的声音：其一，博客的见效周期相当漫长，对中小企业难以见效太快，有些博文甚至因为软文痕迹太明显而达不到宣传效果。其二，对于知名企业，博客可用来倾听消费者呼声，与消费者互动，但投入巨资后，是否有所值，只有企业最清楚。此外，一旦对博客话题监控和引导不力，无异于埋下一颗地雷，甚至引来负面效果。<br />&nbsp;&nbsp;&nbsp; 2006年，沃尔玛在国外向一些影响力巨大的购物博客投资了数百万美元，这些博客发表了诸多对沃尔玛购物有利的分析评论，大大推动了其销量。但这一行为被媒体披露后，造成了大批购物者的拒购，使其品牌形象遭到巨大损害。</p>
<br />
<p><strong>短期内顽疾难除</strong></p>
<br />
<p>&nbsp;&nbsp;&nbsp; 对于博客的流量，国内网民们一度对其持怀疑的态度。有网民就在论坛中发帖称&ldquo;新浪博客默认支持个人造假刷流量，许多博主为了赚钱，公然造假来赚取流量&rdquo;，而淘宝网上也曾出现了专以刷博客流量为生的叫卖，并明码标价。<br />&nbsp;&nbsp;&nbsp; 据调查，IT圈一些博主们更是花钱雇佣专门的公司来刷流量。&ldquo;一次花费几百元就能让你的博客流量翻好几倍。&rdquo;一家专门刷取流量的公司在推销电话中向本刊记者如此表示。今年8月份，新浪博客升级时，就爆出众多点击博客的IP地址&ldquo;都是重复的&rdquo;，使得博客流量造假内幕暴露无遗。<br />&nbsp;&nbsp;&nbsp; 对此，新浪市场销售部营销中心公关李璐表示：&ldquo;对于个别博主可能进行的作弊、欺诈等不诚信行为，我们已经准备了多种有效的反作弊工具，并会对作弊的博主进行处罚。但由于技术有限，现在任何一个Web2.0平台都无法完全屏蔽这样的行为。&rdquo; <br />&nbsp;&nbsp;&nbsp; 方兴东也认为：&ldquo;由于利益诱使及市场的不规范，博客交易现象对个人和企业的名誉都将造成极大损害，而专业的机构很难约束，需靠职业道德来进行自律。&rdquo;<br />&nbsp;&nbsp;&nbsp; 颇有意思的是，就在方兴东接受采访前的8月26日，其个人博文《奥运新媒体大战 新浪一马当先》被推荐到新浪博客首页。在4大门户网站均为北京奥运会期间的视频大战各自吹嘘的时候，方兴东的文章同样因为数据争议而被视为公关软文。难道中国的&ldquo;博客教父&rdquo;也被新浪&ldquo;收买&rdquo;？<br />&nbsp;&nbsp;&nbsp; 有知情博主透露，这是方兴东跟新浪有合作的缘故。另有博主也证实新浪确实曾找过一些知名博主为其写软文，鼓吹其在奥运大战的领先地位。<br />&nbsp;&nbsp;&nbsp; 有利益来往，博客被企业收买的&ldquo;顽疾&rdquo;短期内就难以杜绝，而方兴东所谓的&ldquo;靠职业道德来自律&rdquo;的论断，在可观的真金白银面前，一再被事实证明是无效。</p>
</span></td>
</tr>
</tbody>
</table>
          <br/><br/>
          <span style="color:red;">
            <a href="http://wangdei.javaeye.com/blog/246798#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 25 Sep 2008 19:07:01 +0800</pubDate>
        <link>http://wangdei.javaeye.com/blog/246798</link>
        <guid>http://wangdei.javaeye.com/blog/246798</guid>
      </item>
          <item>
        <title>JAVA分布式应用中使用XML数据</title>
        <author>wangdei</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://wangdei.javaeye.com">wangdei</a>&nbsp;
                    链接：<a href="http://wangdei.javaeye.com/blog/246312" style="color:red;">http://wangdei.javaeye.com/blog/246312</a>&nbsp;
          发表时间: 2008年09月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>可扩展标记语言（XML）作为一种简单的、中性的、易读的数据表示形式已经变得越来越流行，许多软件厂商宣布的&ldquo;支持XML"，意味着他们的产品将能生成或处理XML数据。XML也被看作再企业间交换数据最佳格式。它允许企业在所交换的数据的XML的文档类型定义（Document Type Definitions，DTDs）或模式（Schema）上取得一致。这些DTDs或Schema是独立于企业使用的数据库模式的。 本文将用研究在不同计算机之间通讯与处理XML数据的分布式系统的构建方法，主要是运行在不同的虚拟机上的JAVA应用之间的XML通讯。 <strong>XML通讯 </strong>万维网协会（World Wide Web Consortium, W3C）在XML规范中定义了XML的语法和语义。为了处理XML数据,XML文档必须经过解析。W3C定义了文档对象模型（DOM），它是应用程序员处理XML数据的接口。DOM已经有包括JAVA在内的许多语言的实现。JAVA应用程序可以通过DOM API来访问XML数据。XML解析器将产生XML文档的DOM表示。 图1说明了处理XML文档的JAVA分布式应用的简单模型。这个模型假设数据可以从诸如关系数据库之类的数据源得到。JAVA代码处理数据并最终产生DOM表示，这些代码表示为图中的处理器。 <img title="点击图片可在新窗口打开" src="http://www.ccw.com.cn/htm/app/aprog/02_1_31.gif" alt="" style="CURSOR: pointer" /> 处理器代码将DOM代表的XML数据传给发送者。发送者是与接收者进行XML数据通讯的JAVA代码。接收者JAVA代码来接受XML数据，产生DOM表示的数据并把它传送给另一个处理器。简而言之，发送者和接收者抽象了DOM表示的XML数据的通讯。 发送者和接收者不是在同一个JAVA虚拟机上执行的。他们是通过分布式系统的构件来相连的。无论是接收者还是发送者都既是客户端又是服务器端，两者的数据传输都是双向的。 Xbeans 就像将要看到的一样，在本文中描述的发送者和接收者的三种实现方法都都是通过Xbeans来实现。Xbeans是一种接受XML数据作为输入，处理这个输入然后向下一个Xbeans输出XML结果的软件构件。Xbeans的输入输出都是XML的DOM文档，亦即传送给Xbeans的不是需要XML解析器解析的字符串，而是通过W3C的标准DOM API解析成了文档对象。图2说明了一个Xbeans。 <img title="点击图片可在新窗口打开" src="http://www.ccw.com.cn/htm/app/aprog/02_1_32.gif" alt="" style="CURSOR: pointer" /> Xbeans是JavaBeans,支持封装、重用、连接和客户化Java代码。通过适当的一些Xbeans和JavaBeans的设计工具，我们就能编很少的代码构建非常有用的分布式应用。 Xbeans从IBM的XML的JAVA开发工具包而来，在其上作了少量修改以便更适合分布式的应用。Xbeans能够从www.Xbeans.org的开放源码项目中免费获得。 实现发送方和接收方 下面将介绍用JAVA实现发送者和接收者的三种不同的方法。然后对每种方法作一个简单的分析。 <strong>方法一：用标准的web 服务器 </strong>这种方法将只是简单的将XML作为文本发送给远程计算机上的web服务器。发送方必须将DOM表示的XML转化为文本来与接收方进行通讯。然后，接受方必须将文本还原为DOM表示，如图3： <img title="点击图片可在新窗口打开" src="http://www.ccw.com.cn/htm/app/aprog/02_1_33.gif" alt="" style="CURSOR: pointer" /> 以下代码段用HTTP来实现发送者。这里用到了IBM Java开发包中的DOMWriter类来实现DOM表示到文本XML表示的转换。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>/**偷窥无罪 <a href="http://www.bt285.cn/content.php?id=141970">http://www.bt285.cn/content.php?id=141970</a>
《我的丑娘》  <a href="http://www.bt285.cn/wodechounian/">http://www.bt285.cn/wodechounian/</a></code></pre>
<pre><code>甜性涩爱 <a href="http://www.bt285.cn/1196863/story/story_1.html">http://www.bt285.cn/1196863/story/story_1.html</a></code></pre>
public void documentReady(DOMEvent evt)  
throws XbeansException { 
try { 
URL receiver = new URL (getRemoteURL ()); 
URLConnection receiverConnection = receiver.openConnection(); 
receiverConnection.setDoOutput(true);  
//向发送者打开一个输出流然后发送文本形式的XML数据 
OutputStream out = receiverConnection.getOutputStream(); 
DOMWriter writer = new DOMWriter(); 
writer.setPrintWriter(new PrintWriter(out)); 
writer.documentReady(  
new com.ibm.xml.xpk4j.dom.DOMEvent( this,evt.getDocument())); 
out.close(); // 为结果打开一个输入流 
BufferedReader in = New BufferedReader( 
new InputStreamReader(receiverConnection.getInputStream())); 
// 处理结果："OK" 表示成功；"Exception" 表示输入流串行化异常 
... 
in.close(); 
} catch (Throwable e) { 
e.printStackTrace(System.err); 
} 
} 

</td>
</tr>
</tbody>
</table>
<p>注意到以上的documentReady()方法用remoteURL属性得到服务器上的CGI脚本的URL。为了与HTTP兼容，CGI脚本类用字符串&rdquo;Content-type: text/html"封装接收者的输出。这个脚本然后调用服务器上的the receiverMain()方法。 Main()函数只是简单的实例化接收者然后调用其receiveDocument()方法。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>
import org.xbeans.communication.stdio.receiver.*; 
public class receiverMain { 
static Bean theReceiver = new Bean(); 
public static void main(String[] args) { 
theReceiver.receiveDocument(); 
} 
} 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p>最后receiveDocument()方法的代码段将重新生成DOM表示以便进一步处理。这里用到了IBM的XML解析器。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>
DOMParser parser = new DOMParser(); // 构造解析器 
try { // 调用解析器 
parser.parse(new InputSource(System.in)); 
} catch (Throwable e) { 
throw new XbeansException("","receiver","io error parsing incoming document", 
"io error parsing incoming document "+e); 
} 
//将文档传向下一个bean 
DOMListener.documentReady(new DOMEvent(this,parser.getDocument())); 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p><strong>方法二：通过JAVA远程方法调用串行化文档 </strong>这个方法通过JAVA远程方法调用（JAVA RMI）和DOM串行化（serialization）来从发送者向接收者传输XML DOM 文档。如图4： <img title="点击图片可在新窗口打开" src="http://www.ccw.com.cn/htm/app/aprog/02_1_34.gif" alt="" style="CURSOR: pointer" /> 以下代码用JAVA远程方法调用实现发送方与接受方的通讯. </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>
public void documentReady(DOMEvent evt) throws XbeansException { 
if (DOMListener==null) { 
try { 
DOMListener = (DOMListener)Naming.lookup(getReceiverName()); 
} catch (Exception e) { 
throw new XbeansException( evt.getDocument().getNodeName(), 
"sender", "error obtaining remote receiver", 
"The name may be wrong or the network may be down."); 
} 
} 
DOMListener.documentReady(evt); } 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p>以下是接受方的JAVA 远程方法调用的实现。setName()方法将接受这传送给RMI注册（registry），documentReady()方法仅仅将接收到的文档传送给下一个组件。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<tbody>
<tr>
<td>
<pre><code><table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5"><tbody><tr><td><pre><code>/**偷窥无罪 <a href="http://www.bt285.cn/content.php?id=141970">http://www.bt285.cn/content.php?id=141970</a>
《我的丑娘》  <a href="http://www.bt285.cn/wodechounian/">http://www.bt285.cn/wodechounian/</a></code></pre>
<pre><code>甜性涩爱 <a href="http://www.bt285.cn/1196863/story/story_1.html">http://www.bt285.cn/1196863/story/story_1.html</a></code></pre>
</td>
</tr>
</tbody>
</table>
public void setReceiverName(String newName) { 
try { 
if (receiverName!=null) Naming.unbind(receiverName); 
receiverName = newName; 
Naming.rebind(receiverName, this ); 
} catch( Exception e ) { 
System.out.println( e ); 
} 
} 
 
public void documentReady(Document incomingDocument)  
throws RemoteException, XbeansException { 
if (DOMListener==null) { 
throw new XbeansException(incomingDocument.getNodeName(),"rmiReceiver", 
"next component not established", "The component needs to be configured."); 
} 
DOMListener.documentReady(new DOMEvent(this,incomingDocument)); 
} 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p><strong>方法三：CORBA-IIOP</strong> 第三方法用CORBA-IIOP(CORBA over Internet Inter-ORB Protocol)来传输数据。对象管理组织（OMG）正在建议扩展接口定义语言（IDL）将XML数据类型包括进去。这样，将来CORBA产品将能传输XML数据。如图5所示： <img title="点击图片可在新窗口打开" src="http://www.ccw.com.cn/htm/app/aprog/02_1_35.gif" alt="" style="CURSOR: pointer" /> 以下的OMG IDL给出了发送者和接收者CORBA实现的接口。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>
exception RemoteReceiverException { 
string remoteIdentifier; 
string documentName; 
string componentName; 
string message; 
string moreMessage; 
}; 
 
typedef sequence byteArray; 
interface XMLReceiver { 
void documentReady(in byteArray serializedDocument) 
raises(RemoteReceiverException); 
}; 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p>以下代码用JAVA串行化DOM和CORBA实现发送者。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>
public void documentReady(DOMEvent evt) throws XbeansException { 
Document documentToSend = evt.getDocument(); 
try { 
ByteArrayOutputStream bastream = new ByteArrayOutputStream(); 
ObjectOutputStream p = new ObjectOutputStream(bastream); 
p.writeObject(documentToSend); 
p.flush(); 
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init( new String[0], 
System.getProperties()); 
XMLReceiver receiver = urlToObject(orb,getReceiverURL()); 
receiver.documentReady(bastream.toByteArray()); 
} catch (RemoteReceiverException rre) { 
throw new XbeansException(rre.remoteIdentifier, rre.documentName, 
rre.componentName, rre.message,rre.moreMessage); 
} catch (Throwable e) { 
throw new XbeansException("","sender", "error sending document "+e, 
"error sending document "+e); 
} 
} 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p>以下代码用JAVA串行化DOM和CORBA实现接收者。 </p>
<table cellspacing="0" border="1" bgcolor="#cccccc" width="100%" cellpadding="5">
<br />
<tbody>
<tr>
<td>
<pre><code>
public void documentReady(byte[] serializedDocument)throws RemoteReceiverException { 
// 反串行化字节流 
ByteArrayInputStream bais = new ByteArrayInputStream(serializedDocument); 
Document theDocument; 
try { 
ObjectInputStream ois = new ObjectInputStream(bais); 
theDocument = (Document)ois.readObject(); 
} catch(Throwable e) { 
throw new RemoteReceiverException(corbaName,"incoming document","receiver", 
"error deserializing document","error deserializing document"+e); 
} 
try { //将文档传向监听者 
local.DOMListener.documentReady(new DOMEvent(this,theDocument)); 
} catch (XbeansException xbe) { 
throw new RemoteReceiverException( xbe.remoteIdentifier(), 
xbe.documentName(),xbe.componentName(), 
xbe.message(),xbe.moreMessage()); 
} 
} 
</code></pre>
</td>
</tr>
</tbody>
</table>
<p><strong>分析:</strong> 测试表明，纯文本表示的XML要比DOM串行化表示性能更好。同时，解析DOM和文本所用的时间也要比用JAVA直接串行化和法串行化所用的时间少。 标准的web服务器方式的优势是其应用基础要广泛许多。CGI脚本能够在绝大多数web服务器上运行，而且，接受方能够很容易的通过URL标识。而对于RMI，则需要RMI注册。CORBA的解决办法则需要在服务器上安装对象请求代理（Object Request Broker，ORB ）,而且，CORBA发送者的实现使用的是一个URL的命名模式而不是接收者的CORBA对象引用，用一个字符串与一个URL相联系，然后在客户端转化。 CORBA 和RMI支持JAVA 客户端到JAVA服务器的解决方案。没有CGI脚本也不需要从标准输入中读取编码异常。而且，不需要在发送者每次用XML通讯时都启动一个JAVA虚拟机。他们两则均支持接收者的自动激活。 JAVA RMI方式只能在JAVA代码之间工作，对于web服务器包括CORBA理论上能在任何编程语言之间通讯。对于JAVA串行化的DOM来说，即便是客户端和服务器端均需要是JAVA代码的要求不是问题，它还存在另外一个困难，即JAVA串行化要求客户端和服务器运行的是相同的DOM实现。 <strong>结论 </strong>正如上面所述，有许多方法可以实现在JAVA分布式应用中发送XML数据，每一种方法的性能和互操作性都是不同的。重要的是应该把XML通讯从分布式应用逻辑中抽取出来。也就是，实现发送和接受XML的代码应和应用逻辑的代码中分离出来。通过把代码打包成软件组件，就能够改变发送方和接受方的代码而不会影响到应用其余实现。 </p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://wangdei.javaeye.com/blog/246312#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 24 Sep 2008 15:14:25 +0800</pubDate>
        <link>http://wangdei.javaeye.com/blog/246312</link>
        <guid>http://wangdei.javaeye.com/blog/246312</guid>
      </item>
          <item>
        <title>如何注意HttpSession的线程</title>
        <author>wangdei</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://wangdei.javaeye.com">wangdei</a>&nbsp;
                    链接：<a href="http://wangdei.javaeye.com/blog/241458" style="color:red;">http://wangdei.javaeye.com/blog/241458</a>&nbsp;
          发表时间: 2008年09月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="highlighter"><ol class="highlighter-j">
<li><span>HttpSession&nbsp;session&nbsp;=&nbsp;&nbsp;request.getSession();</span> </li>
<li class="alt"><span>List&lt;Product&gt;&nbsp;list&nbsp;=&nbsp;session.getAttribute(</span><span class="string"><span style="color: #a31515;">"productCart"</span></span><span>);</span> </li>
<li><span>myService.save(list);&nbsp;</span><span style="color: #008200;"><span class="comment">//&nbsp;保存购物车数据到数据库</span></span> </li>
</ol></div>
<p><br />这个对象会被多次使用，也会被同一个用户的多个页面使用，所以他对于系统来说是线程<strong><span style="color: #ff0000;">不安全</span></strong>的。 呵呵。<a href="http://www.bt285.cn/content.php?id=141970" target="_self">偷窥无罪</a><br /><br />比如用户在从产品列表里面选择产品，这面选择3种，他点了查看购物车 <br />该用户还开了另一个页面，继续选择产品。 <br /><br />此时，在显示购物车的页面，有可能运行在一半时，其已经选择的产品列表，并另一个页面的操作修改了。所以显示的产品数量有可能并不是3种。 <br /><br />因为session需要维持当前用户的信息，所以其在多个线程里是共享的。所以是线程不安全的。 <br /><br /><br />不过，这个是表面现象，我们只要正确使用事务，保证数据的准确性，表面的问题可以不用管它。 <br /><br />我们可以把session里面的数据另外保存到一个新的数据对象里，这个对象不再因为session的改变而出现变动。这个对象传递给业务层进行事务处理，保证数据级别的准确。 <br /><br />千万不要把session,或者 session里面的对象直接传递给业务层，因为你的业务处理一半时，同样可能出现session对象被改变的情况。有可能造成重要数据出现偏差。 <br /><strong>举例：</strong> <br /><br />session 对应三个产品， <br />事务里面循环了产品，并计算了总价格， <br />计算完毕，准备保存时，session变了，产品变成了4个。 <br />此时开始保存。产品保存了4个，可总价格却还是3个的。 <br /><br />出现了数据不一致。 <br /><br /><strong>修改后的例子</strong> <br />session 对应三个产品 <br />重新生成一个产品对象数组，把session数据复制过来，然后传递给业务层 <br />事务里面循环计算总价格 <br />计算完毕，此时session变了，但并不影响我们这个新的产品数组对象 <br />保存，三个产品，价格也正确。</p>
<div class="highlighter"><ol class="highlighter-j">
<li><span>HttpSession&nbsp;session&nbsp;=&nbsp;&nbsp;request.getSession();</span> </li>
<li class="alt"><span>List&lt;Product&gt;&nbsp;&nbsp;list&nbsp;=&nbsp;session.getAttribute(</span><span class="string"><span style="color: #a31515;">"productCart"</span></span><span>);</span> </li>
<li><span>List&lt;Product&gt;&nbsp;listNew&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #0000ff;">new</span></strong></span><span>&nbsp;ArrayList&lt;Product&gt;();</span> </li>
<li class="alt"><span>Product&nbsp;pNew;</span> </li>
<li><span class="keyword"><strong><span style="color: #0000ff;">for</span></strong></span><span>(Product&nbsp;p&nbsp;:&nbsp;list){</span> </li>
<li class="alt"><span>&nbsp;&nbsp;pNew&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #0000ff;">new</span></strong></span><span>&nbsp;Product();</span> </li>
<li><span>&nbsp;&nbsp;pNew.setProductId(p.getProductId());</span> </li>
<li class="alt"><span>&nbsp;&nbsp;</span><span style="color: #008200;"><span class="comment">//&nbsp;其它的复制参数的语句</span></span> </li>
<li><span>&nbsp;&nbsp;listNew.add(pNew);&nbsp;</span><span style="color: #008200;"><span class="comment">//&nbsp;保存到新的列表里面</span></span> </li>
<li class="alt"><span>}</span> </li>
<li><span>myService.save(listNew);&nbsp;</span><span style="color: #008200;"><span class="comment">//&nbsp;保存购物车数据到数据库，这个是安全的</span></span></li>
</ol></div>
<p><br /><strong>总结： </strong><br />有些线程安全问题是很隐蔽的，等你出了问题，很可能根本不认为会是那里出的问题。记住一点，Java里面的对象传递的是对象的引用，只要2个地方用了相同的引用，则其它地方的变动，这一面也会变动。</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://wangdei.javaeye.com/blog/241458#comments" style="color:red;">已有 <strong>2</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 13 Sep 2008 15:12:07 +0800</pubDate>
        <link>http://wangdei.javaeye.com/blog/241458</link>
        <guid>http://wangdei.javaeye.com/blog/241458</guid>
      </item>
          <item>
        <title>红黑树(red-black tree)算法，附AVL树的比较 </title>
        <author>wangdei</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://wangdei.javaeye.com">wangdei</a>&nbsp;
                    链接：<a href="http://wangdei.javaeye.com/blog/236157" style="color:red;">http://wangdei.javaeye.com/blog/236157</a>&nbsp;
          发表时间: 2008年09月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>红黑树的定义</strong> <br /><br />正如在CLRS中定义的那样（译者： CLRS指的是一本著名的算法书Introduction to Algorithms，中文名应该叫算法导论，CLRS是该书作者Cormen, Leiserson, Rivest and Stein的首字母缩写），一棵红黑树是指一棵满足下述性质的二叉搜索树（BST, binary search tree）： <br /><br />1. 每个结点或者为黑色或者为红色。 <br />2. 根结点为黑色。 <br />3. 每个叶结点(实际上就是NULL指针)都是黑色的。 <br />4. 如果一个结点是红色的，那么它的两个子节点都是黑色的（也就是说，不能有两个相邻的红色结点）。 <br />5. 对于每个结点，从该结点到其所有子孙叶结点的路径中所包含的黑色结点数量必须相同。 <br /><br />数据项只能存储在内部结点中（internal node）。我们所指的"叶结点"在其父结点中可能仅仅用一个NULL指针表示，但是将它也看作一个实际的结点有助于描述红黑树的插入与删除算法，叶结点一律为黑色。 <br /><br /><strong>定理</strong>：一棵拥有n个内部结点的红黑树的树高h&lt;=2log(n+1) <br /><br />(译者：我认为原文中的有关上述定理的证明是错误的，下面的证明方法是参考CLRS中的证明写出的。） <br /><br /><strong>证明</strong>：首先定义一颗红黑树的黑高度Bh为：从这颗红黑树的根结点（但不包括这个根结点）到叶结点的路径上包含的黑色结点（注意，包括叶结点）数量。另外规定叶结点的黑高度为0。 <br />下面我们首先证明一颗有n个内部结点的红黑树满足n&gt;=2^Bh-1。这可以用数学归纳法证明，施归纳于树高h。当h=0时，这相当于是一个叶结点，黑高度Bh为0，而内部结点数量n为0，此时0&gt;=2^0-1成立。假设树高h&lt;=t时，n&gt;=2^Bh-1成立，我们记一颗树高为t+1的红黑树的根结点的左子树的内部结点数量为nl，右子树的内部结点数量为nr，记这两颗子树的黑高度为Bh'（注意这两颗子树的黑高度必然一样），显然这两颗子树的树高&lt;=t，于是有nl&gt;=2^Bh'-1以及nr&gt;=2^Bh'-1，将这两个不等式相加有nl+nr&gt;=2^(Bh'+1)-2，将该不等式左右加1，得到n&gt;=2^(Bh'+1)-1，很显然Bh'+1&gt;=Bh，于是前面的不等式可以变为n&gt;=2^Bh-1，这样就证明了一颗有n个内部结点的红黑树满足n&gt;=2^Bh-1。 <br />下面我们完成剩余部分的证明，记红黑树树高为h。我们先证明Bh&gt;=h/2。在任何一条从根结点到叶结点的路径上（不包括根结点，但包括叶结点），假设其结点数量为m，注意其包含的黑色结点数量即为Bh。当m为偶数时，根据性质5可以看出每一对儿相邻的结点至多有一个红色结点，所以有Bh&gt;=m/2；而当m为奇数时，这条路径上除去叶结点后有偶数个结点，于是这些结点中的黑色结点数B'满足B'&gt;=(m-1)/2，将该不等式前后加1得出Bh&gt;=(m+1)/2，可以进一步得出Bh&gt;m/2，综合m为偶数的情况可以得出Bh&gt;=m/2，而m在最大的情况下等于树高h，因此可以证明Bh&gt;=h/2。将Bh&gt;=h/2代入n&gt;=2^Bh-1，最终得到h&lt;=2log(n+1)。证明完毕。 <br /><br /><br />本文余下的内容将阐释如何在不破坏红黑树性质的前提下进行结点的插入与删除，以及为什么插入与删除的处理次数与树高是成比例的，或者说是O(log n)。 <br /><br /><strong>Okasaki插入方法</strong> <br /><br />首先用与二叉搜索树一样的方法将一个结点插入到红黑树中，并且颜色为红色。(这个新结点的子结点将是叶结点，根据定义，这些叶结点是黑色的。)此时，我们将或者破坏了性质2（根结点为黑色）或者破坏了性质4（不能有两个相邻的红色结点）。 <br /><br />如果新插入的结点是根结点的话（这意味着在插入前该红黑树是空的），我们仅仅将这个结点的颜色改为黑色，插入操作就完成了。 <br /><br />如果性质4遭到了破坏，这一定是由于新插入结点的父结点也是红色造成的。由于红黑树的根结点必须是黑色的，因此新插入的结点一定会存在一个祖父结点，并且根据性质4这个祖父结点必然是黑色的。此时，由新插入结点的祖父结点为根的子树的结构一共有四种可能性（译者，前面这句话我没有看明白原文，我是用我的理解写出来的，如果有误请指正。），如下面的图解所示。在Okasaki插入方法中，每一种可能出现的子树都被转换为图解正中间的那种子树形式。 <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/okasaki.png" alt="" /> <br /><br />（A,B,C与D表示任意的子树。我们曾经说过新插入结点的子结点一定是叶结点，但很快我们就会看到上面的图解适用于更普遍的情况）。 <br /><br />首先，请注意在变换的过程中&lt;AxByCzD&gt;的顺序保持不变。 <br /><br />另外，注意该变换不会改变从这颗子树的父结点到这颗子树中任何一个叶结点的路径中黑色结点的数量（当然前提是这颗子树有父结点）。我们再一次遇到了这样的情形：即该红黑树只有可能违反性质2（如果y是根结点）或性质4（如果y的父结点是红色的），但这次变换带来了一个好处，即我们现在距离红黑树的根结点靠近了两步。我们可以重复这种操作直到：或者y的父结点为黑色，在这种情况下插入操作完成；或者y成为根结点，在此情况下我们将y染为黑色后插入操作完成。（将根结点染为黑色会对每条从根结点到叶结点的路径增加相同数量的黑色结点，因此如果在染色操作之前性质5没有遭到破坏那么操作之后也不会。） <br /><br />上述步骤保持了红黑树的性质，并且所花的时间与树高是成比例的，也即O(log n)。 <br /><br />旋转 <br /><br />在红黑树中进行结构调整的操作常常可以用更清晰的术语"旋转"操作来表达，图解如下。 <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/rotate.png" alt="" /> <br /><br />很显然，在旋转操作中&lt;AxByC&gt;的顺序保持不变。因此，如果操作前该树是一颗二叉搜索树，而且结构调整时只使用了旋转操作，那么调整后该树仍然是一颗二叉搜索树。在本文的余下部分，我们将仅仅使用旋转操作对树进行调整，因此我们无须再言明关于如何保持树中元素的正确排序问题。 <br /><br />在下面的图解中，Okasaki插入方法中的变换操作被表示为一个或者两个旋转操作。 <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/okasaki-rot.png" alt="" /> <br /><br /><strong>CLRS插入方法</strong> <br /><br />CLRS中给出了一种比Okasaki插入方法更复杂但效率稍高的插入方法。它的时间复杂度仍然是O(log n)，但在大O中的常数要更小一些。 <br /><br />CLRS插入方法与Okasaki插入方法一样都是从标准的二叉搜索树插入操作开始的，并且将这个新插入的结点染为红色，它们的区别在于如何处理遭到破坏的性质4（不能存在两个相邻的红色结点）。我们要根据下端红色结点的叔叔结点的颜色区分两种情况。（下端红色结点是指在一对儿红色父结点／红色子结点中的那个子结点。）让我们先考虑叔叔结点为黑色的情况。根据每个红色结点是其父结点的左子结点还是右子结点，这种情况可以分为四种子情况。下面的图解展示了如何调整红黑树以及如何重新染色。 <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/black-uncle-ins.png" alt="" /> <br /><br />在这里我们感兴趣的是上面图解中的方法与Okasaki方法的比较。它们有两点不同。第一点是关于如何对最终的子树（图解中间的那个子树）进行染色的。在Okasaki方法中，这颗子树的根结点y被染成红色而它的子结点被染成黑色，然而在CLRS方法中y被染成了黑色而它的子结点被染成了红色。将y染成黑色意味着红黑树性质4（不能存在两个相邻的红色结点）不会在y这一点遭到破坏，因此对树的调整不需要向根结点的方向继续进行下去。在此情况下，CLRS插入方法最多需要进行两次旋转操作即可完成插入。 <br /><br />第二点不同是在这种情况下CLRS方法必须满足一个先决条件，即下端红色结点的叔叔结点必须是黑色的。在上面的图解中我们可以很清楚地看出，如果那个叔叔结点（即子树A或者D的根结点）是红色的，那么最终的树中将存在两个相邻的红色结点，因此这种方法不能适用于叔叔结点为红色的情况。 <br /><br />下面我们考虑下端红色结点的叔叔结点为红色的情况。在这种情况下我们将上端红色结点和它的兄弟结点（即下端红色结点的叔叔结点）染为黑色并且将它们的父结点染为红色。树的结构并没有进行调整。这时根据下端红色结点是其父结点的左子结点还是右子结点以及上端红色结点是其父结点的左子结点还是右子结点可以分出四种情况，但是这四种情况从本质上来说都是相同的。下面图解只描述了一种情况： <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/red-uncle-ins.png" alt="" /> <br /><br />很容易看出，在这种操作的过程中从树的根结点到叶结点的路径中的黑色结点数量没有发生变化。在此操作之后，红黑数的性质只有可能在该子树的根结点同时也是整个树的根结点或者该子树的父结点是红色的情况下才会遭到破坏。换句话说，我们又将开始重复上述操作，但我们距离树的根结点又靠近了两步。照这样不断重复该步骤直到：或者(i)z的父结点为黑色，此时插入操作结束；(ii)z成为根结点，我们将它染为黑色之后插入操作结束；或者(iii)我们遇到了下端红色结点的叔叔结点为黑色的情况，这时我们只要做一或两次旋转操作即可完成插入。在最坏的情况下，我们必须对新插入的结点到根结点的路径上的每个结点进行染色操作，此时需要的操作数为O(log n)。 <br /><br /><strong>删除</strong> <br /><br />为了从红黑树中删除一个结点，我们将从一颗标准二叉搜索树的删除操作开始（参见CLRS，第12章）。我们回顾一下标准二叉搜索树的删除操作的三种情况： <br /><br />1. 要删除的结点没有子结点。在这种情况下，我们直接将它删除就可以了。如果这个结点是根结点，那么这颗树将成为空树；否则，将它的父结点中相应的子结点指针赋值为NULL。 <br />2. 要删除的结点有一个子结点。与上面一样，直接将它删除。如果它是根结点，那么它的子结点变为根结点；否则，将它的父结点中相应的子结点指针赋值为被删除结点的子结点的指针。 <br />3. 要删除的结点有两个子结点。在这种情况下，我们先找到这个结点的后继结点（successor），也就是它的右子树中最小的那个结点。然后我们将这两个结点中的数据元素互换，之后删除这个后继结点。由于这个后继结点不可能有左子结点，因此删除该后继结点的操作必然会落入上面两种情况之一。 <br /><br />注意，在树中被删除的结点并不一定是那个最初包含要删除的数据项的那个结点。但出于重建红黑树性质的目的，我们只关心最终被删除的那个结点。我们称这个结点为v，并称它的父结点为p(v)。 <br /><br />v的子结点中至少有一个为叶结点。如果v有一个非叶子结点，那么v在这颗树中的位置将被这个子结点取代；否则，它的位置将被一个叶结点取代。我们用u来表示二叉搜索树删除操作后在树中取代了v的位置的那个结点。如果u是叶结点，那么我们可以确定它是黑色的。 <br /><br />如果v是红色的，那么删除操作就完成了－－－因为这种删除不会破坏红黑树的任何性质。所以，我们下面假定v是黑色的。删除了v之后，从根结点到v的所有子孙叶结点的路径将会比树中其它的从根结点到叶结点的路径拥有更少的黑色结点，这会破坏红黑树的性质5。另外，如果p(v)与u都是红色的，那么性质4也会遭到破坏。但实际上我们解决性质5遭到破坏的方案在不用作任何额外工作的情况下就可以同时解决性质4遭到破坏的问题，所以从现在开始我们将集中精力考虑性质5的问题。 <br /><br />让我们在头脑中给u打上一个黑色记号（black token）。这个记号表示从根结点到这个带记号结点的所有子孙叶结点的路径上都缺少一个黑色结点（在一开始，这是由于v被删除了）。我们会将这个记号一直朝树的顶部移动直到性质5重新恢复。在下面的图解中用一个黑色的方块表示这个记号。如果带有这个记号的结点是黑色的，那么我们称之为双黑色结点（doubly black node）。 <br /><br />注意这个记号只是一个概念上的东西，在树的数据结构中并不存在物理实现。 <br /><br />我们要区分四种不同的情况。 <br /><br />A. 如果带记号的结点是红色的或者它是树的根结点（或两者皆是），只要将它染为黑色就可以完成删除操作。注意，这样就会恢复红黑树的性质4（不能存在两个相邻的红色结点）。而且，性质5也会被恢复，因为这个记号表示从根结点到该结点的所有子孙叶结点的路径需要增加一个黑色结点以便使这些路径与其它的根结点到叶结点路径所包含的黑色结点数量相同。通过将这个红色结点改变为黑色，我们就在这些缺少一个黑色结点的路径上添加了一个黑色结点。 <br /><br />如果带记号的结点是根结点并且为黑色，那么直接将这个标记丢掉就可以了。在这种情况下，树中每条从根结点到叶结点的路径的黑色结点数量都比删除操作前少了一个，并且依旧保持住了性质5。 <br /><br />在余下的情况里，我们可以假设这个带记号的结点是黑色的，并且不是根结点。 <br /><br />B. 如果这个双黑色结点的兄弟结点以及两个侄子结点都是黑色的，那么我们就将它的兄弟结点染为红色之后将这个记号朝树根的方向移动一步。 <br /><br />下面的图解展示了两种可能出现的子情况。环绕y的虚线表示在此并我们不关心y的颜色，而在A,B,C和D的上面的小圆圈表示这些子树的根结点是黑色的（译者：注意这个双黑色结点必然会有两个非叶结点的侄子结点。这是因为这个双黑色结点的记号表示从根结点到该结点的所有子孙叶结点的路径中的黑色结点数量都比其它的根结点到叶结点路径所包含的黑色结点数量少1，而该双黑色结点本身就是一个黑色结点，因此从它的兄弟结点到其子孙叶结点的路径上的黑色结点数量必然要大于1，我们很容易看出如果其兄弟结点的任何一个子结点为叶结点的话这一点是不可能满足的，因此这个双黑色结点的必然会有两个非叶结点的侄子结点）。 <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/case-b.png" alt="" /> <br /><br />将那个兄弟结点染为红色，就会从所有到该结点的子孙叶结点的路径上去掉一个黑色结点，因此现在这些路径上的黑色结点数量与到双黑色结点的子孙叶结点的路径上的黑色结点数量一致了。我们将这个记号向上移动到y，这表明现在所有到y的子孙叶结点的路径上缺少一个黑色结点。此时问题仍然没有得到解决，但我们又向树根推进了一步。 <br /><br />很显然，只有带记号的结点的两个侄子结点都是黑色时才能进行上述操作，这是因为如果有一个侄子结点是红色的那么该操作会导致出现两个相邻的红色结点。 <br /><br />C. 如果带记号的结点的兄弟结点是红色的，那么我们就进行一次旋转操作并改变结点颜色。下面的图解展示了两种可能出现的情况： <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/case-c.png" alt="" /> <br /><br />注意上面的操作并不会改变从根结点到任何叶结点路径上的黑色结点数量，并且它确保了在操作之后这个双黑色结点的兄弟结点是黑色的，这使得后续的操作或者属于情况B，或者属于情况D。 <br /><br />由于这个记号比起操作前离树的根结点更远了，所以看起来似乎我们向后倒退了。但请注意现在这个双黑色结点的父结点是红色的了，所以如果下一步操作属于情况B，那么这个记号将会向上移动到那个红色结点，然后我们只要将它染为黑色就完成了。此外，下面将会展示，在情况D下，我们总是能够将这个记号消耗掉从而完成删除操作。因此这种表面上的倒退现象实际上意味着删除操作就快要完成了。 <br /><br />D. 最终，我们遇到了双黑色结点有一个黑色兄弟结点并至少一个侄子结点是红色的情况。我们下面给出一个结点x的近侄子结点（near nephew）的定义：如果x是其父结点的左子结点，那么x的兄弟结点的左子结点为x的近侄子结点，否则x的兄弟结点的右子结点为x的近侄子结点；而另一个侄子结点则为x的远侄子结点（far nephew）。（在下面的图解中可以看出，x的近侄子结点要比它的远侄子结点距离x更近。） <br /><br />现在我们会遇到两种子情况：(i)双黑色结点的远侄子结点是黑色的，在此情况下它的近侄子结点一定是红色的；(ii)远侄子结点是红色的，在此情况下它的近侄子结点可以为任何颜色。如下面的图解所示，子情况(i)可以通过一次旋转和变色转换为子情况(ii)，而在子情况(ii)下只要通过一次旋转和变色就可以完成删除操作。根据双黑色结点是其父结点的左子结点还是右子结点，下面图解中的两行显示出两种对称的形式。 <br /><br /><img src="http://sage.mc.yu.edu/kbeen/teaching/algorithms/resources/case-d.png" alt="" /> <br /><br />在这种情况下我们生成了一个额外的黑色结点，记号被丢掉，删除操作完成。从上面图解中很容易看出，所有到带记号结点的子孙叶结点的路径上的黑色结点数量增加了1，而其它的路径上的黑色结点数量保持不变。很显然，在此刻红黑树的任何性质都没有遭到破坏。 <br /><br />将上面的所有情况综合起来，我们可以看出在最坏的情况下我们必须沿着从叶结点到根结点的路径每次都执行常量次数的操作，因此删除操作的时间复杂度为O(log n)。</p>
<div>&nbsp;</div>
<div>附&nbsp; AVL树的比较</div>
<div>&nbsp;</div>
<div>我还一直沉浸于2.4的AVL树,殊不知,早已经是过年货了(各位新春愉快!!),2.6已经使用Red-Black-Tree,感叹 <br />不是我不明白,世界变得太快,既然是AVL树则比较一下: <br />简介: <br />AVL树又称高度平衡的二叉搜索树,是1962年由两位俄罗斯的数学家G.M.Adel'son-Vel,sky和E.M.Landis提出 <br />的.引入二叉树的目的是为了提高二叉树的搜索的效率,减少树的平均搜索长度.为此,就必须每向二叉树插入 <br />一个结点时调整树的结构,使得二叉树搜索保持平衡,从而可能降低树的高度,减少的平均树的搜索长度. <br /><br />AVL树的定义: <br />一棵AVL树满足以下的条件: <br />1&gt;它的左子树和右子树都是AVL树 <br />2&gt;左子树和右子树的高度差不能超过1 <br />从条件1可能看出是个递归定义,如GNU一样. <br /><br />性质: <br />1&gt;一棵n个结点的AVL树的其高度保持在0(log2(n)),不会超过3/2log2(n+1) <br />2&gt;一棵n个结点的AVL树的平均搜索长度保持在0(log2(n)). <br />3&gt;一棵n个结点的AVL树删除一个结点做平衡化旋转所需要的时间为0(log2(n)). <br /><br />从1这点来看红黑树是牺牲了严格的高度平衡的优越条件为代价红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外，由于它的设计，任何不平衡都会在三次旋转之内解决。当然，还有一些更好的，但实现起来更复杂的数据结构能够做到一步旋转之内达到平衡，但红黑树能够给我们一个比较&ldquo;便宜&rdquo;的解决方案。红黑树的算法时间复杂度和AVL相同，但统计性能比AVL树更高. <br />看看人家怎么评价的: <br />AVL trees are actually easier to implement than RB trees because there are fewer cases. And AVL trees require O(1) rotations on an insertion, whereas red-black trees require O(lg n). <br />In practice, the speed of AVL trees versus red-black trees will depend on the data that you're inserting. If your data is well distributed, so that an unbalanced binary tree would generally be acceptable (i.e. roughly in random order), but you want to handle bad cases anyway, then red-black trees will be faster because they do less unnecessary rebalancing of already acceptable data.On the other hand, if a pathological insertion order (e.g. increasing order of key) is common, then AVL trees will be faster, because the stricter balancing rule will reduce the tree's height. <br />Splay trees might be even faster than either RB or AVL trees,depending on your data access distribution. And if you can use a hash instead of a tree, then that'll be fastest of all. <br /><br />试着翻译一下: <br />&lt;pre&gt; <br />由于AVL树种类较少所以比红黑树实际上更容易实现.而且ALV树在旋转插入所需要的复杂度为0(1),而红 <br />黑树则需要的复杂度为0(lgn). <br />实际上插入AVL树和红黑树的速度取决于你所插入的数据.如果你的数据分布较好,则比较宜于采用AVL树(例如随机产生系列数),但是如果你想处理比较杂乱的情况,则红黑树是比较快的,因为红黑树对已经处理好的数据重新平衡减少了不心要的操作.另外一方面,如果是一种非寻常的插入系列比较常见(比如,插入密钥系列),则AVL树比较快,因为它的严格的平衡规则将会减少树的高度. <br />Splay树可能比红黑树和AVL树还要快这也取决于你所访问的数据分布,如果你用哈希表来代替一棵树,则 <br />将所以的树还要快. <br />&lt;/pre&gt; <br />Splay树是什么树,我不是很清楚,我没有详细的查阅. <br /><br />感受一下带来的变革 <br />//--&gt;&lt;pre&gt; <br />/* <br />* 翻一下老皇历(2.4) <br />*/ <br />struct vm_area_struct* find_vma(struct mm_struct* mm,unsigned long addr) <br />{ <br />struct vm_area_struct* vma = NULL; <br />if(mm) <br />{ <br />/* <br />* check the cache first. <br />*/ <br />/* <br />* (Check hit rate is typically around 35%.) <br />*/ <br />/* <br />* 首先查找一下最近一次访问的虚地址空间是不否是 CACHE中 <br />*/ <br />vma = mm-&gt;mmap_cache; <br />if(!(vma &amp;&amp; vma-&gt;vm_end &gt; addr &amp;&amp; vma-&gt;vm_start&lt;addr)) <br />{ <br />/* <br />* miss hit 未命中,继续查找线性表或者是AVL树 <br />*/ <br />if(!mm-&gt;mmap_val) <br />{ <br />/* <br />* go though the liner list <br />*/ <br />vma = mm-&gt;mmap; <br />while(vma &amp;&amp; vma-&gt;vm_end &lt;= addr) <br />{ <br />vma = vam-&gt;vma_next; <br />} <br />} <br />else <br />{ <br />/* <br />* Then go though the AVL tree quickly <br />*/ <br />struct vm_area_struct* tree = mm-&gt;mmap_avl; <br />vam = NULL; <br />for(;;) <br />{ <br />if(tree == vm_avl_empty) <br />{ <br />/* <br />* 结点为空,失败 <br />*/ <br />break; <br />} <br />if(tree-&gt;vm_end &gt; addr) <br />{ <br />vma = tree; <br />if(tree-&gt;vm_start &lt;= addr) <br />{ <br />/* <br />* 找到,快速退出循环 <br />*/ <br />break; <br />} <br />tree = tree-&gt;vm_avl_left; <br />} <br />else <br />{ <br />tree = tree-&gt;vm_avl_right; <br />} <br />} <br />} <br />if(vma) <br />{ <br />/* <br />* 查找成功,记在CACHE中 <br />*/ <br />mm-&gt;mmap_cache = vma; <br />} <br />} <br />} <br />return vma; <br /><br />} <br /><br />//&lt;--&lt;/pre&gt; <br /><br />/* <br />* 再贴新年历(2.6) <br />*/ <br />//--&gt;&lt;pre1&gt; <br />struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr) <br />{ <br />struct vm_area_struct *vma = NULL; <br /><br />if (mm) { <br />/* Check the cache first. */ <br />/* (Cache hit rate is typically around 35%.) */ <br />/* <br />* 首先查找一下最近一次访问的虚地址空间是不否是 CACHE中 <br />*/ <br />vma = mm-&gt;mmap_cache; <br />if (!(vma &amp;&amp; vma-&gt;vm_end &gt; addr &amp;&amp; vma-&gt;vm_start &lt;= addr)) { <br />struct rb_node * rb_node; <br /><br />/* <br />* miss hit 未命中,直接查找red-black tree <br />*/ <br />rb_node = mm-&gt;mm_rb.rb_node; <br />vma = NULL; <br /><br />while (rb_node) { <br />struct vm_area_struct * vma_tmp; <br /><br />vma_tmp = rb_entry(rb_node, <br />struct vm_area_struct, vm_rb); <br /><br />if (vma_tmp-&gt;vm_end &gt; addr) { <br />vma = vma_tmp; <br />if (vma_tmp-&gt;vm_start &lt;= addr) <br />break; <br />rb_node = rb_node-&gt;rb_left; <br />} else <br />rb_node = rb_node-&gt;rb_right; <br />} <br />/* <br />* 查找成功,记在CACHE中 <br />*/ <br />if (vma) <br />mm-&gt;mmap_cache = vma; <br />} <br />} <br />return vma; <br />} <br /><br />//&lt;--&lt;/pre1&gt; <br /><br />在这儿只是做了一些小的方面的比较和在内核中的真正的应用,很多的地方没有分析到,还 <br />望各位同仁多多指正和拓展. <br /></div>
          <br/><br/>
          <span style="color:red;">
            <a href="http://wangdei.javaeye.com/blog/236157#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 03 Sep 2008 15:01:36 +0800</pubDate>
        <link>http://wangdei.javaeye.com/blog/236157</link>
        <guid>http://wangdei.javaeye.com/blog/236157</guid>
      </item>
          <item>
        <title>ThreadLocal相关知识2</title>
        <author>wangdei</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://wangdei.javaeye.com">wangdei</a>&nbsp;
                    链接：<a href="http://wangdei.javaeye.com/blog/236153" style="color:red;">http://wangdei.javaeye.com/blog/236153</a>&nbsp;
          发表时间: 2008年09月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>ThreadLocal与synchronized <br />Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉，而对ThreadLocal就要陌生得多了。 <br />并发问题。当一个对象被两个线程同时访问时，可能有一个线程会得到不可预期的结果。 <br /><br />一个简单的java类Studnet</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&nbsp;Student&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age=</span><span class="number"><span style="color: #c00000;">0</span></span><span>; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;getAge()&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">return</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.age; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;setAge(</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.age&nbsp;=&nbsp;age; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">public class Student {
  private int age=0;
  
  public int getAge() {
	  return this.age;
	  
  }
  
  public void setAge(int age) {
	  this.age = age;
  }
}</pre>
<p><br />一个多线程类ThreadDemo. <br />这个类有一个Student的私有变量,在run方法中，它随机产生一个整数。然后设置到student变量中,从student中读取设置后的值。然后睡眠5秒钟，最后再次读student的age值。 <br /></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&nbsp;ThreadDemo&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">implements</span></strong></span><span>&nbsp;Runnable{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;Student&nbsp;student&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Student(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">static</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;main(String[]&nbsp;agrs)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ThreadDemo&nbsp;td&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;ThreadDemo(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;t1&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Thread(td,</span><span class="string"><span style="color: #0000ff;">"a"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;t2&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Thread(td,</span><span class="string"><span style="color: #0000ff;">"b"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;t1.start(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;t2.start(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span class="comment"><span style="color: #008200;">/*&nbsp;(non-Javadoc) </span></span>&nbsp;</li>
<li><span><span class="comment"><span style="color: #008200;">&nbsp;*&nbsp;@see&nbsp;java.lang.Runnable#run() </span></span>&nbsp;</span></li>
<li><span><span class="comment"><span style="color: #008200;">&nbsp;*/</span></span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;run()&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;accessStudent(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;accessStudent()&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;currentThreadName&nbsp;=&nbsp;Thread.currentThread().getName(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;is&nbsp;running!"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment"><span style="color: #008200;">//&nbsp;System.out.println("first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"+this.student.getAge()); </span></span><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Random(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age&nbsp;=&nbsp;random.nextInt(</span><span class="number"><span style="color: #c00000;">100</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;set&nbsp;age&nbsp;to:"</span></span><span>+age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.setAge(age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;second&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">public class ThreadDemo implements Runnable{
  Student student = new Student();
  public static void main(String[] agrs) {
	 ThreadDemo td = new ThreadDemo();
	 Thread t1 = new Thread(td,"a");
	 Thread t2 = new Thread(td,"b");
    t1.start();
    t2.start();

  }
/* (non-Javadoc)
 * @see java.lang.Runnable#run()
 */
 public void run() {
	 accessStudent();
 }
 
 public void accessStudent() {
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	   // System.out.println("first  read age is:"+this.student.getAge());
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	     
 }
  
}</pre>
<p>运行这个程序,屏幕输出如下: <br />a is running! <br />b is running! <br />thread b set age to:33 <br />thread b first read age is:33 <br />thread a set age to:81 <br />thread a first read age is:81 <br />thread b second read age is:81 <br />thread a second read age is:81 <br /><br />需要注意的是，线程a在同一个方法中，第一次读取student的age值与第二次读取值不一致。这就是出现了并发问题。 <br /><br />synchronized <br />上面的例子，我们模似了一个并发问题。Java提供了同步机制来解决并发问题。synchonzied关键字可以用来同步变量，方法，甚至同步一个代码块。 <br />使用了同步后，一个线程正在访问同步对象时，另外一个线程必须等待。 <br />Synchronized同步方法 <br />现在我们可以对accessStudent方法实施同步。 <br />public synchronized void accessStudent() <br />再次运行程序，屏幕输出如下： <br />a is running! <br />thread a set age to:49 <br />thread a first read age is:49 <br />thread a second read age is:49 <br />b is running! <br />thread b set age to:17 <br />thread b first read age is:17 <br />thread b second read age is:17 <br /><br />加上了同步后，线程b必须等待线程a执行完毕后，线程b才开始执行。 <br /><br />对方法进行同步的代价是非常昂贵的。特别是当被同步的方法执行一个冗长的操作。这个方法执行会花费很长的时间,对这样的方法进行同步可能会使系统性能成数量级的下降。 <br /><br />Synchronized同步块 <br />在accessStudent方法中，我们真实需要保护的是student变量，所以我们可以进行一个更细粒度的加锁。我们仅仅对student相关的代码块进行同步。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">synchronized</span></strong></span><span>(</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>)&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Random(); &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age&nbsp;=&nbsp;random.nextInt(</span><span class="number"><span style="color: #c00000;">100</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;set&nbsp;age&nbsp;to:"</span></span><span>+age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.setAge(age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">	    synchronized(this) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }</pre>
<p>运行方法后，屏幕输出： <br />a is running! <br />thread a set age to:18 <br />thread a first read age is:18 <br />b is running! <br />thread a second read age is:18 <br />thread b set age to:62 <br />thread b first read age is:62 <br />thread b second read age is:62 <br /><br />需要特别注意这个输出结果。 <br />这个执行过程比上面的方法同步要快得多了。 <br />只有对student进行访问的代码是同步的，而其它与部份代码却是异步的了。而student的值并没有被错误的修改。如果是在一个真实的系统中，accessStudent方法的操作又比较耗时的情况下。使用同步的速度几乎与没有同步一样快。 <br /><br />使用同步锁 <br />稍微把上面的例子改一下，在ThreadDemo中有一个私有变量count，。 <br />private int count=0; <br />在accessStudent()中, 线程每访问一次，count都自加一次, 用来记数线程访问的次数。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count++; &nbsp;&nbsp;</span></li>
<li><span>Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>}</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }</pre>
<p>为了模拟线程，所以让它每次自加后都睡眠5秒。 <br />accessStuden()方法的完整代码如下：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span>&nbsp;&nbsp;&nbsp;String&nbsp;currentThreadName&nbsp;=&nbsp;Thread.currentThread().getName(); &nbsp;&nbsp;</span></span></li>
<li><span>System.out.println(currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;is&nbsp;running!"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count++; &nbsp;&nbsp;</span></li>
<li><span>Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>}</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;read&nbsp;count:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">synchronized</span></strong></span><span>(</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Random(); &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age&nbsp;=&nbsp;random.nextInt(</span><span class="number"><span style="color: #c00000;">100</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;set&nbsp;age&nbsp;to:"</span></span><span>+age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.setAge(age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span>System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;second&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge());&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">   	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
			    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
		    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(this) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());</pre>
<p>运行程序后，屏幕输出: <br />a is running! <br />b is running! <br />thread a read count:2 <br />thread a set age to:49 <br />thread a first read age is:49 <br />thread b read count:2 <br />thread a second read age is:49 <br />thread b set age to:7 <br />thread b first read age is:7 <br />thread b second read age is:7 <br /><br />我们仍然对student对象以synchronized(this)操作进行同步。 <br />我们需要在两个线程中共享count失败。 <br /><br />所以仍然需要对count的访问进行同步操作。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">synchronized</span></strong></span><span>(</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>)&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count++; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;}</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;read&nbsp;count:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">synchronized</span></strong></span><span>(</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Random(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age&nbsp;=&nbsp;random.nextInt(</span><span class="number"><span style="color: #c00000;">100</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;set&nbsp;age&nbsp;to:"</span></span><span>+age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.setAge(age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;second&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">long</span></strong></span><span>&nbsp;endTime&nbsp;=&nbsp;System.currentTimeMillis(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">long</span></strong></span><span>&nbsp;spendTime&nbsp;=&nbsp;endTime&nbsp;-&nbsp;startTime; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"花费时间："</span></span><span>+spendTime&nbsp;+</span><span class="string"><span style="color: #0000ff;">"毫秒"</span></span><span>);&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">		 synchronized(this) {
	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(this) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	    long endTime = System.currentTimeMillis();
	    long spendTime = endTime - startTime;
	    System.out.println("花费时间："+spendTime +"毫秒");</pre>
<p><br />程序运行后，屏幕输出 <br />a is running! <br />b is running! <br />thread a read count:1 <br />thread a set age to:97 <br />thread a first read age is:97 <br />thread a second read age is:97 <br />花费时间：10015毫秒 <br />thread b read count:2 <br />thread b set age to:47 <br />thread b first read age is:47 <br />thread b second read age is:47 <br />花费时间：20124毫秒 <br /><br />我们在同一个方法中，多次使用synchronized(this)进行加锁。有可能会导致太多额外的等待。 <br />应该使用不同的对象锁进行同步。 <br /><br />设置两个锁对象，分别用于student和count的访问加锁。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span>&nbsp;Object&nbsp;studentLock&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Object(); &nbsp;&nbsp;</span></span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span>&nbsp;Object&nbsp;countLock&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Object(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>accessStudent()方法如下： &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">long</span></strong></span><span>&nbsp;startTime&nbsp;=&nbsp;System.currentTimeMillis(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;currentThreadName&nbsp;=&nbsp;Thread.currentThread().getName(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;is&nbsp;running!"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment"><span style="color: #008200;">//&nbsp;System.out.println("first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"+this.student.getAge()); </span></span><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">synchronized</span></strong></span><span>(countLock)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count++; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;read&nbsp;count:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.count); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">synchronized</span></strong></span><span>(studentLock)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Random(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age&nbsp;=&nbsp;random.nextInt(</span><span class="number"><span style="color: #c00000;">100</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;set&nbsp;age&nbsp;to:"</span></span><span>+age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.setAge(age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;second&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+</span><span class="keyword"><strong><span style="color: #7f0055;">this</span></strong></span><span>.student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">long</span></strong></span><span>&nbsp;endTime&nbsp;=&nbsp;System.currentTimeMillis(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">long</span></strong></span><span>&nbsp;spendTime&nbsp;=&nbsp;endTime&nbsp;-&nbsp;startTime; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"花费时间："</span></span><span>+spendTime&nbsp;+</span><span class="string"><span style="color: #0000ff;">"毫秒"</span></span><span>);&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java"> private Object studentLock = new Object();
private Object countLock = new Object();

accessStudent()方法如下：
	 long startTime = System.currentTimeMillis();
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	   // System.out.println("first  read age is:"+this.student.getAge());

		 synchronized(countLock) {
	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(studentLock) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	    long endTime = System.currentTimeMillis();
	    long spendTime = endTime - startTime;
	    System.out.println("花费时间："+spendTime +"毫秒");</pre>
<p><br />这样对count和student加上了两把不同的锁。 <br /><br />运行程序后，屏幕输出： <br />a is running! <br />b is running! <br />thread a read count:1 <br />thread a set age to:48 <br />thread a first read age is:48 <br />thread a second read age is:48 <br />花费时间：10016毫秒 <br />thread b read count:2 <br />thread b set age to:68 <br />thread b first read age is:68 <br />thread b second read age is:68 <br />花费时间：20046毫秒 <br />与两次使用synchronized(this)相比，使用不同的对象锁，在性能上可以得到更大的提升。 <br /><br />由此可见synchronized是实现java的同步机制。同步机制是为了实现同步多线程对相同资源的并发访问控制。保证多线程之间的通信。 <br />可见，同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销，所以同步操作应该是细粒度的。如果同步使用得当，带来的性能开销是微不足道的。使用同步真正的风险是复杂性和可能破坏资源安全,而不是性能。 <br /><br /><br />ThreadLocal <br />由上面可以知道，使用同步是非常复杂的。并且同步会带来性能的降低。Java提供了另外的一种方式，通过ThreadLocal可以很容易的编写多线程程序。从字面上理解，很容易会把ThreadLocal误解为一个线程的本地变量。其它ThreadLocal并不是代表当前线程，ThreadLocal其实是采用哈希表的方式来为每个线程都提供一个变量的副本。从而保证各个线程间数据安全。每个线程的数据不会被另外线程访问和破坏。 <br /><br />我们把第一个例子用ThreadLocal来实现,但是我们需要些许改变。 <br />Student并不是一个私有变量了，而是需要封装在一个ThreadLocal对象中去。调用ThreadLocal的set方法，ThreadLocal会为每一个线程都保持一份Student变量的副本。所以对student的读取操作都是通过ThreadLocal来进行的。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">protected</span></strong></span><span>&nbsp;Student&nbsp;getStudent()&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;Student&nbsp;student&nbsp;=&nbsp;(Student)studentLocal.get(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">if</span></strong></span><span>(student&nbsp;==&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">null</span></strong></span><span>)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Student(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;studentLocal.set(student); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">return</span></strong></span><span>&nbsp;student; &nbsp;&nbsp;</span></li>
<li><span>} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span class="keyword"><strong><span style="color: #7f0055;">protected</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;setStudent(Student&nbsp;student)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;studentLocal.set(student); &nbsp;&nbsp;</span></li>
<li><span>}&nbsp;&nbsp;</span></li>
</ol></div>
<pre name="code" class="java">	protected Student getStudent() {
		Student student = (Student)studentLocal.get();
		if(student == null) {
			student = new Student();
			studentLocal.set(student);
		}
		return student;
	}
	
	protected void setStudent(Student student) {
		studentLocal.set(student);
	}</pre>
<p><br />accessStudent()方法需要做一些改变。通过调用getStudent()方法来获得当前线程的Student变量，如果当前线程不存在一个Student变量，getStudent方法会创建一个新的Student变量，并设置在当前线程中。 <br />Student student = getStudent(); <br />student.setAge(age); <br />accessStudent()方法中无需要任何同步代码。 <br /><br />完整的代码清单如下： <br />TreadLocalDemo.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a href="http://www.javaeye.com/topic/81936?page=1#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div>
</div>
<ol class="dp-j">
<li><span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&nbsp;TreadLocalDemo&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">implements</span></strong></span><span>&nbsp;Runnable&nbsp;{ &nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">final</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">static</span></strong></span><span>&nbsp;&nbsp;ThreadLocal&nbsp;studentLocal&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;ThreadLocal(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">static</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;main(String[]&nbsp;agrs)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreadLocalDemo&nbsp;td&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;TreadLocalDemo(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;t1&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Thread(td,</span><span class="string"><span style="color: #0000ff;">"a"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;t2&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Thread(td,</span><span class="string"><span style="color: #0000ff;">"b"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t1.start(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t2.start(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment"><span style="color: #008200;">/*&nbsp;(non-Javadoc) </span></span>&nbsp;</li>
<li><span><span class="comment"><span style="color: #008200;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;java.lang.Runnable#run() </span></span>&nbsp;</span></li>
<li><span><span class="comment"><span style="color: #008200;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span></span><span>&nbsp;&nbsp;</span></span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;run()&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;accessStudent(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span>&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span>&nbsp;&nbsp;accessStudent()&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;currentThreadName&nbsp;=&nbsp;Thread.currentThread().getName(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;is&nbsp;running!"</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span>&nbsp;Random(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">int</span></strong></span><span>&nbsp;age&nbsp;=&nbsp;random.nextInt(</span><span class="number"><span style="color: #c00000;">100</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;set&nbsp;age&nbsp;to:"</span></span><span>+age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Student&nbsp;student&nbsp;=&nbsp;getStudent(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.setAge(age); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName+</span><span class="string"><span style="color: #0000ff;">"&nbsp;first&nbsp;&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">try</span></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number"><span style="color: #c00000;">5000</span></span><span>); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">catch</span></strong></span><span>(InterruptedException&nbsp;ex)&nbsp;{ &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff;">"thread&nbsp;"</span></span><span>+currentThreadName&nbsp;+</span><span class="string"><span style="color: #0000ff;">"&nbsp;second&nbsp;read&nbsp;age&nbsp;is:"</span></span><span>+student.getAge()); &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
<li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055;">protected</span></strong></span><span>&n