??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
该得的,即侥幸巧取也不可能长久保有。如果我们看清这个事实,许多所?人生的重
大抉择就可以淡然处之Q根本无需焦虑。而所?人生的困?Q也往往当下变得无?br />
挂?br />
我自己就是一个活生生的例子。从一q大学就军_不再늠I所Q所以,大学四年的时
间多半在念h文科学的东西。毕业后工作了几q_才决定要늠I所。硕士毕业后Q立
下决心:从此不再为文凭而念书。谁知道Q世事难料,当了五年讲师后,我又被时势所
q,出国念博士?nbsp;
出国Ӟ一位大学同学笑我:全班最晚念博士的都要回国了Q你现在才要出去Q两q后
我从剑桥回来Q觉得h生际遇无常,莫此为甚Q一个从大一决定再也不钻营学位的h
Q竟然连士和博士都拿到了!属于我们该得的,哪样曄过Q而h生中该得与不?br />
得的I竟有多,我们又何曄晓?从此我对际遇一事不能不更加淡然?nbsp;
当讲师期_有些态度较极端的学生会当面表现出他们的不屑;从剑桥回来时Q却被学
生当做不得了的事看待。这U表面上的大起大落,其实都是好事者之aQ完全看不到?br />
实的真相。从表面上看来,两年拿到剑桥博士,q好像很了不赗但是,在这两年?br />
前我已经花整整一q_研I主题有关的论文全部看完Qƈ扑և研究方向Q而之前更?br />
׃q时间做控制斚w的研IӞq且在国际着名的学术期刊中发表论文。而从士毕业
到拿博士Q期间七q的旉我从不停止过研究与自修?nbsp;所以,q个博士其实是篏U了?br />
q的成果Q或者,只算我花在控制学门的旉Q也臛_有五q_Q根本也没什么好惊讶
的?nbsp;
思h不从长期而持l的累积q程来看待生命因U蓄而有的成果,老爱在表面上以断裂?br />
孤立的事件夸大议论,因此每每在^淡无奇的事g上强做悲喜。可是对我来Ԍ当讲?br />
期间被学生瞧不vQ以及剑桥刚回来时被同学夸大本事Q都只是表象。事实是Q我只在
乎每天二十四时点点滴滴的篏U?nbsp;
拿硕士或博士只是特定时刻里这些成果篏U的外在展示而已Qh生命中真实的累积从不
曑֛q些事g而终止或d?nbsp;
常有学生满怀忧虑的问我:"老师Q我很想先当完兵Q工作一两年再考研I所。这样好?br />
Q?
"很好Q这样子有机会先用实务来印证学理Q你늠I所时会比别Z解自p的是什?br />
?
"可是Q我怕当完兵又工作后Q会失去斗志Q因此考不上研I所?
"那你先考研I所好了?
"可是Q假如我先念研究所Q我怕自己又会像念大学时一栯Ӟ因此늚不甘不愿的?br />
"
"那你q是先去工作好了Q?nbsp;"
"可是。。。。。。?nbsp;
我完全可以体会到他们的焦虑,可是却无法压抑住对于q种话的感慨。其实,说穿了他
所需要的是两年研究所加两q工作,以便加深知识的深q度和获取实务经验?nbsp;
先工作或先升学,表面上大相迳庭,其实骨子里的差别Ҏ可以忽略?nbsp;
?朝三暮四"q个成语故事里,Mh原本喂养猴子的橡实是"早上四颗下午三颗"Q后?br />
改ؓ"朝三暮四"Q猴子就不高兴而坚持改回到"朝四暮三"。其实,先工作或先升学,?br />
间差异就有如"朝三暮四"?朝四暮三"Q原不值得计较。但是,我们l常看不到这U生
命过E中长远而持l的累积Q老爱一旉遇中的小差别夸大到生L关的地步?nbsp;
最讽刺的是Q当我们面对两个可能的方案,而焦虑得不知如何抉择Ӟ通常表示q两?br />
Ҏ可能一样好Q或者一样坏Q因而实际上选择哪个都一P唯一的差别只是先后之?br />
而已。而且Q愈是让我们焦虑得厉害的Q其实差别越,愈不值得焦虑。反而真正有?br />
昄好坏差别Ӟ我们L的就知道该怎么做了。可是我们却l常看不到长q的来Q?br />
短视的盯着两案短期内的得失Q想选甲案,p不得乙案的好处;想选乙案,又舍不得
甲案的好处。如果看得够q,人生长则八、九十,短则五、六十年Q先做哪一件事又有
什么关p?甚至当完兵又工作后,再花一整年准备研究所Q又有什么了不vQ当Ӟ?br />
些hq是会忧虑说Q?我当完兵又工作后Q会不会因ؓ家篏或记忆力衰退而比较难考上?br />
I所Q? 我只能这样回{:"一个h考不上研I所Q只有两个可能:或者他不够聪明Q或
者他的确够聪明。不够聪明而考不上,那也没什么好抱怨的。假如你够聪明,q考不?br />
研究所Q那只能说你的决心不够强。假如你是决心不够强Q就表示你生命中q有其他?br />
可能性,光要程度ƈ不下于硕士学位,而你舍不得丢下他。既然如此,考不上研I所
也无L到遗憾。不是吗Q?人生的\q么多,Z么要老斤斤计较着一个可能性?
我高中最要好的朋友,一生背q:高中考两ơ,高一念两ơ,大学又考两ơ,甚至q机
车驾照都考两ơ。毕业后Q他告诉自己Q我没有关系Q也没有学历Q只能靠加倍的诚恳
和努力。现在,他自己拥有一家公司,q收入数千万?nbsp;
一个h在升学过E中不顺利,而在事业上顺利,q是常见的事。有才华的hQ不会因?br />
被名校拒l而连带失M的才华,只不q要另外N合他表现的场所而已。反q来Q一
个h在升学过E中太顺利,也难免因而放不下w段d业,而只能乖乖领薪水q活?nbsp;
兮兮Q谁人知晓? 我们又有什么好得意Q又有什么好忧虑Qh生的得与失,有时?br />
怎么也说不清楚,有时候却再简单不q了Q我们得到^日篏U的成果Q而失L们不?br />
努力累积的!所以重要的不是和别人比成就Q而是努力d自己惛_的。最后该得到?br />
不会你一分,不该得到的也不会多你一分?nbsp;
好像是前q的时候,我遇C位高中同学。他在南加大当电机系的副教授Q被清华甉|
聘回来开短期评。从高中时代他就很用功,以第一志愿上台大电机后Q四q都拿书?br />
奖,怿他在专业上的研究也已卓然有成。回想高中入学时Q我们两个h的智力测验成
l分居全学年W一Q第二名。可是从高一我就不曾攑ּ自己喜欢的文学,音乐Q书法,
艺术和哲学,而他却始l不曑ֈ心,因此两个人在学术上的差距只会愈来愈远。反q来
_q十几二十年我在人文领域所获得的满I恐怕已q非他能理解的了。我太太问过
我,如果我肯全心专注于一个研I域,是不是至会赶上q位同学的成?我不q样
惻I两个不同性情的hQ注定要C条不同的路。不该得的东西,我们注定是得不到?br />
Q随随便便拿两个人来比,只看C所得到的,却看不到他所失去的,q有什么意义?
有次清华电台讉K我:"老师你如何面对你人生中的困境Q?我当场愣在那里,怎么样都
想不出我q一生什么时候有q困境!后来仔细回想Q才发现Q我不是没有q困境,而是
被常人当?困境"的境遇,我都当作一时的际遇Q不曑֜意过而已。刚服完兵役Ӟ?br />
子已出生却还找不到工作。我曄虑过Q却又觉得迟早会有工作,报酬也不至于低的?br />
谱,不曾太放在心上。念士期间Q家计全靠太太的薪水Q省吃P用,Ҏ而言又算?br />
上困境。一来精上我过的很充实Q二来我知道q一切是Z让自己有Z转行L?br />
(做自己想做的?。三十一岁才要出国,而同学正要回pMLQ我很紧?不知道剑?br />
要求的有多严)Q却不曾丧气。因为,我知道自pM直很努力Q也有很满意的心得和
成果Q只不过别h看不到而已。
我没有过困境Q因为我从不在乎外在的得失,也不武断的和别h比高下,而只在乎自己
内在真实的篏U?nbsp;
我没有过困境Q因为我实了解刎ͼ生命是一U长期而持l的累积q程Q绝不会因ؓ?br />
一的事件而有剧烈的v伏。
同时我也怿Q属于我们该得的Q迟早会得到Q属于我们不该得的,即一分也不可?br />
增加。假如你可以持有相同的信念,那么人生于你也会是宽q而长q,没有什么了不得
?困境"Q也没有什么好焦虑的了?
]]>
]]>
]]>
下面q一文章是JavaEYE里的Qhttp://www.javaeye.com/topic/83978?page=1
ClassLoader一个经常出现又让很多h望而却步的词,本文试图以最显易懂的方式来讲解 ClassLoaderQ希望能对不了解该机制的朋友起到一点点作用?
要深入了解ClassLoaderQ首先就要知道ClassLoader是用来干什么的Q顾名思义Q它是用来加蝲Class文g到JVMQ以供程序用的。我们知道,javaE序可以动态加载类定义Q而这个动态加载的机制是通过ClassLoader来实现的Q所以可惌知ClassLoader的重要性如何?
看到q里Q可能有的朋友会惛_一个问题,那就是既然ClassLoader是用来加载类到JVM中的Q那么ClassLoader又是如何被加载呢Q难道它不是java的类Q?
没有错,在这里确实有一个ClassLoader不是用java语言所~写的,而是JVM实现的一部分Q这个ClassLoader是bootstrap classloaderQ启动类加蝲器)Q这个ClassLoader在JVMq行的时候加载java核心的API以满javaE序最基本的需求,其中包括用户定义的ClassLoaderQ这里所谓的用户定义是指通过javaE序实现的ClassLoaderQ一个是ExtClassLoaderQ这个ClassLoader是用来加载java的扩展API的,也就?lib/ext中的c,一个是AppClassLoaderQ这个ClassLoader是用来加载用h器上CLASSPATH讄目录中的Class的,通常在没有指定ClassLoader的情况下Q程序员自定义的cdpClassLoaderq行加蝲?
当运行一个程序的时候,JVM启动Q运行bootstrap classloaderQ该ClassLoader加蝲java核心APIQExtClassLoader和AppClassLoader也在此时被加载)Q然后调用ExtClassLoader加蝲扩展APIQ最后AppClassLoader加蝲CLASSPATH目录下定义的ClassQ这是一个程序最基本的加载流E?
上面大概讲解了一下ClassLoader的作用以及一个最基本的加载流E,接下来将讲解一下ClassLoader加蝲的方式,q里׃得不讲一下ClassLoader在这里用了双亲委托模式q行cd载?
每一个自定义ClassLoader都必ȝ承ClassLoaderq个抽象c,而每个ClassLoader都会有一个parent ClassLoaderQ我们可以看一下ClassLoaderq个抽象cM有一个getParent()ҎQ这个方法用来返回当前ClassLoader的parentQ注意,q个parent不是指的被承的c,而是在实例化该ClassLoader时指定的一个ClassLoaderQ如果这个parent为nullQ那么就默认该ClassLoader的parent是bootstrap classloaderQ这个parent有什么用呢?
我们可以考虑q样一U情况,假设我们自定义了一个ClientDefClassLoaderQ我们用这个自定义的ClassLoader加蝲java.lang.StringQ那么这里String是否会被q个ClassLoader加蝲呢?事实上java.lang.Stringq个cdƈ不是被这个ClientDefClassLoader加蝲Q而是由bootstrap classloaderq行加蝲Qؓ什么会q样Q实际上q就是双亲委托模式的原因Q因为在M一个自定义ClassLoader加蝲一个类之前Q它都会先委托它的父亲ClassLoaderq行加蝲Q只有当父亲ClassLoader无法加蝲成功后,才会p己加载,在上面这个例子里Q因为java.lang.String是属于java核心API的一个类Q所以当使用ClientDefClassLoader加蝲它的时候,该ClassLoader会先委托它的父亲ClassLoaderq行加蝲Q上面讲q,当ClassLoader的parent为nullӞClassLoader的parent是bootstrap classloaderQ所以在ClassLoader的最层是bootstrap classloaderQ因此最l委托到bootstrap classloader的时候,bootstrap classloader׃q回String的Class?
我们来看一下ClassLoader中的一D|代码Q?/p>
另外一是Qhttp://dev.csdn.net/article/68/68103.shtm
静态库、动态连接库
E序~制一般需l编辑、编译、连接、加载和q行几个步骤。在我们的应用中Q有一些公׃码是需要反复用,把q些代码~译?#8220;?#8221;文gQ在q接步骤中,q接器将从库文g取得所需的代码,复制到生成的可执行文件中。这U库UCؓ静态库Q其特点是可执行文g中包含了库代码的一份完整拷贝;~点是被多ơ用就会有多䆾冗余拯?/p>
Z克服q个~点可以采用动态连接库。这个时候连接器仅仅是在可执行文件中打上标志Q说明需要用哪些动态连接库Q当q行E序Ӟ加蝲器根据这些标志把所需的动态连接库加蝲到内存?/p>
另外在当前的~程环境中,一般都提供Ҏ让程序在q行的时候把某个特定的动态连接库加蝲q运行,也可以将其卸载(例如Win32的LoadLibrary()&FreeLibrary()和Posix的dlopen()&dlclose()Q。这个功能被q泛地用于在E序q行时刻更新某些功能模块或者是E序外观?/p>
What is ClassLoader?
与普通程序不同的是,JavaE序Qclass文gQƈ不是本地的可执行E序。当q行JavaE序Ӟ首先q行JVMQJava虚拟机)Q然后再把Java class加蝲到JVM里头q行Q负责加载Java class的这部分叫做Class Loader?/p>
JVM本n包含了一个ClassLoaderUCؓBootstrap ClassLoaderQ和JVM一PBootstrap ClassLoader是用本地代码实现的,它负责加载核心Java ClassQ即所有java.*开头的c)。另外JVMq会提供两个ClassLoaderQ它们都是用Java语言~写的,由Bootstrap ClassLoader加蝲Q其中Extension ClassLoader负责加蝲扩展的Java classQ例如所有javax.*开头的cd存放在JRE的ext目录下的c)QApplication ClassLoader负责加蝲应用E序自n的类?/p>
When to load the class?
什么时候JVM会用ClassLoader加蝲一个类呢?当你使用javaL行一个类QJVM使用Application ClassLoader加蝲q个c;然后如果cA引用了类BQ不是直接引用q是用Class.forName()引用QJVM׃扑ֈ加蝲cA的ClassLoaderQƈ用这个ClassLoader来加载类B?/p>
Why use your own ClassLoader?
gJVM自n的ClassLoader已经_了,Z么我们还需要创qClassLoader呢?
因ؓJVM自带的ClassLoader只是懂得从本地文件系l加载标准的java class文gQ如果编写你自己的ClassLoaderQ你可以做到Q?br />
1Q在执行非置信代码之前,自动验证数字{
2Q动态地创徏W合用户特定需要的定制化构建类
3Q从特定的场所取得java classQ例如数据库?br />
4) {等
事实上当使用Applet的时候,qC特定的ClassLoaderQ因旉要从|络上加载java classQƈ且要查相关的安全信息?/p>
目前的应用服务器大都使用了ClassLoader技术,即你不需要创qClassLoaderQ了解其原理也有助于更好地部|自q应用?/p>
ClassLoader Tree & Delegation Model
当你军_创徏你自qClassLoaderӞ需要承java.lang.ClassLoader或者它的子cR在实例化每个ClassLoader对象Ӟ需要指定一个父对象Q如果没有指定的话,pȝ自动指定ClassLoader.getSystemClassLoader()为父对象。如下图Q?/p>
在Java 1.2后,java class的加载采用所谓的委托模式QDelegation ModleQ,当调用一个ClassLoader.loadClass()加蝲一个类的时候,遵循以下的步骤Q?br />
1Q检查这个类是否已经被加载进来了Q?br />
2Q如果还没有加蝲Q调用父对象加蝲该类
3Q如果父对象无法加蝲Q调用本对象的findClass()取得q个cR?/p>
所以当创徏自己的Class LoaderӞ只需要重载findClass()q个Ҏ?/p>
Unloading? Reloading?
当一个java class被加载到JVM之后Q它有没有可能被卸蝲呢?我们知道Win32有FreeLibrary()函数QPosix有dlclose()函数可以被调用来卸蝲指定的动态连接库Q但是Javaq没有提供一个UnloadClass()的方法来卸蝲指定的类?/p>
在Java中,java class的卸载仅仅是一U对pȝ的优化,有助于减应用对内存的占用。既然是一U优化方法,那么完全是JVM自行军_如何实现Q对Java开发h员来说是完全透明的?/p>
在什么时候一个java class/interface会被卸蝲呢?Sun公司?a >原话是这么说的:"class or interface may be unloaded if and only if its class loader is unreachable. Classes loaded by the bootstrap loader may not be unloaded."
事实上我们关心的不是如何卸蝲cȝQ我们关心的是如何更新已l被加蝲了的cM而更新应用的功能。JSP则是一个非常典型的例子Q如果一个JSP文g被更改了Q应用服务器则需要把更改后的JSP重新~译Q然后加载新生成的类来响应后l的h?/p>
其实一个已l加载的cL无法被更新的Q如果你试图用同一个ClassLoader再次加蝲同一个类Q就会得到异常(java.lang.LinkageError: duplicate class definitionQ,我们只能够重新创Z个新的ClassLoader实例来再ơ加载新cR至于原来已l加载的c,开发h员不必去它Q因为它可能q有实例正在被用,只要相关的实例都被内存回收了Q那么JVM׃在适当的时候把不会再用的cd载?/p>
?/span>入锁Q?span class="hilite1" style="background-color: #ffff00; ">ReentrantLockQ是一U递归无阻塞的同步机制。以前一直认为它是synchronized的简单替代,而且实现机制也不相差太远。不q最q实践过E中发现它们之间q是有着天壤之别?/p>
以下?a target="_blank" style="color: #006699; text-decoration: underline; ">官方说明Q一个可重入的互斥锁?LockQ它h与?synchronized Ҏ和语句所讉K的隐式监视器锁定相同的一些基本行为和语义Q但功能更强大?span class="hilite1" style="background-color: #ffff00; ">ReentrantLock 由最q成功获得锁定,q且q没有释放该锁定的线E所拥有。当锁定没有被另一个线E所拥有Ӟ调用 lock 的线E将成功获取该锁定ƈq回。如果当前线E已l拥有该锁定Q此Ҏ立卌回。可以?isHeldByCurrentThread() ?getHoldCount() Ҏ来检查此情况是否发生?/p>
它提供了lock()ҎQ?br /> 如果该锁定没有被另一个线E保持,则获取该锁定q立卌回,锁定的保持计数讄?1?br /> 如果当前U程已经保持该锁定,则将保持计数?1Qƈ且该Ҏ立即q回?br /> 如果该锁定被另一个线E保持,则出于线E调度的目的Q禁用当前线E,q且在获得锁定之前,该线E将一直处于休眠状态,此时锁定保持计数被设|ؓ 1?/p>
最q在研究Java concurrent中关于Q务调度的实现ӞM延迟队列DelayQueue的一些代码,比如take()。该Ҏ的主要功能是从优先队列(PriorityQueueQ取Z个最应该执行的Q务(最优|Q如果该d的预订执行时间未刎ͼ则需要waitq段旉差。反之,如果旉CQ则q回该Q务。而offer()Ҏ是将一个Q务添加到该队列中?/p>
后来产生了一个疑问:如果最应该执行的Q务是一个小时后执行的,而此旉要提交一?0U后执行的Q务,会出C么状况?q是先看看take()的源代码Q?/p>
而以下是offer()的源代码:
如代码所C,take()和offer()都是lock了重入锁。如果按照synchronized的思维Q用诸如synchronized(obj)的方法)Q这两个Ҏ是互斥的。回到刚才的疑问Qtake()Ҏ需要等?个小时才能返回,而offer()需要马上提交一?0U后q行的Q务,会不会一直等待take()q回后才能提交呢Q答案是否定的,通过~写验证代码也说明了q一炏V这让我寚w入锁有了更大的兴,它确实是一个无d的锁?/p>
下面的代码也许能说明问题Q运行了4个线E,每一ơ运行前打印lock的当前状态。运行后都要{待5U钟?/p>
q是它的输出Q?br />
Pre ReentrantLock@a59698[Unlocked] 每一个线E的锁状态都?#8220;Unlocked”,所以都可以q行。但在把con.awaitҎThread.sleep(5000)Ӟ输出变成了Q?br />
Pre ReentrantLock@a59698[Unlocked] 以上的对比说明线E在{待?con.await)Q已l不在拥有(keepQ该锁了Q所以其他线E就可以获得重入锁了?br />
有必要会q头再看看Java官方的解释:“如果该锁定被另一个线E保持,则出于线E调度的目的Q禁用当前线E,q且在获得锁定之前,该线E将一直处于休眠状?#8221;。我对这里的“保持”的理解是指非wait状态外的所有状态,比如U程Sleep、for循环{一切有CPU参与的活动。一旦线E进入wait状态后Q它׃再keepq个锁了Q其他线E就可以获得该锁Q当该线E被唤醒Q触发信h者timeoutQ后Q就接着执行Q会重新“保持”锁,当然前提依然是其他线E已l不?#8220;保持”了该重入锁?/p>
ȝ一句话Q对于重入锁而言Q?lock"?keep"是两个不同的概念。lock了锁Q不一定keep锁,但keep了锁一定已llock了锁?/p>
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Pre ReentrantLock@a59698[Unlocked]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Pre ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-1]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-2]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-3]
Post ReentrantLock@a59698[Locked by thread pool-1-thread-4]
十五q前Q多处理器系l是高度专用pȝQ要p数十万美元(大多数具有两个到四个处理器)。现在,多处理器pȝ很便宜,而且数量很多Q几乎每个主要微处理器都内置了多处理支持Q其中许多系l支持数十个或数百个处理器?/p>
要用多处理器系l的功能Q通常需要用多U程构造应用程序。但是正如Q何编写ƈ发应用程序的人可以告诉你的那P要获得好的硬件利用率Q只是简单地在多个线E中分割工作是不够的Q还必须保U程实大部分时间都在工作,而不是在{待更多的工作,或等待锁定共享数据结构?/p>
如果U程之间 ?/em>需要协调,那么几乎没有d可以真正地ƈ行。以U程池ؓ例,其中执行的Q务通常怺独立。如果线E池利用公共工作队列Q则从工作队列中删除元素或向工作队列d元素的过E必LU程安全的,q且q意味着要协调对头、尾或节炚w链接指针所q行的访问。正是这U协调导致了所有问题?/p>
?Java 语言中,协调对共享字D늚讉K的传l方法是使用同步Q确保完成对׃n字段的所有访问,同时h适当的锁定。通过同步Q可以确定(假设cȝ写正)h保护一l给定变量的锁定的所有线E都拥有对q些变量的独占访问权Qƈ且以后其他线E获得该锁定Ӟ可以看到对q些变量q行的更攏V弊端是如果锁定竞争太厉宻IU程常常在其他线E具有锁定时要求获得该锁定)Q会损害吞吐量,因ؓ竞争的同步非常昂c(Public Service AnnouncementQ对于现?JVM 而言Q无竞争的同步现在非怾宜?/p>
Z锁定的算法的另一个问题是Q如果gq具有锁定的U程Q因为页面错误、计划gq或其他意料之外的gq)Q则 没有要求获得该锁定的U程可以l箋q行?/p>
q可以用可变变量来以比同步更低的成本存储共享变量,但它们有局限性。虽然可以保证其他变量可以立即看到对可变变量的写入,但无法呈现原子操作的?修改-写顺序,q意味着Q比如说Q可变变量无法用来可靠地实现互斥Q互斥锁定)或计数器?/p>
假如开发线E安全的计数器类Q那么这暴?
许多q发法中都昄了原子的?修改-写组合。清?2 中的代码实现了简单的互斥Q?
清单 1 中的计数器类可以可靠地工作,在竞争很或没有竞争旉可以很好地执行。然而,在竞争激烈时Q这大大损x能Q因?JVM 用了更多的时间来调度U程Q管理竞争和{待U程队列Q而实际工作(如增加计数器Q的旉却很。您可以回想 上月专栏中的图,该图昄了一旦多个线E用同步竞争一个内|监视器Q吞吐量如何大q度下降。虽然该专栏说明了新? 使用锁定Q如果一个线E试图获取其他线E已l具有的锁定Q那么该U程被dQ直到该锁定可用。此Ҏh一些明昄~点Q其中包括当U程被阻塞来{待锁定Ӟ它无法进行其他Q何操作。如果阻塞的U程是高优先U的dQ那么该Ҏ可能造成非常不好的结果(UCؓ 优先U倒置的危险)?/span> 使用锁定q有一些其他危险,如死锁(当以不一致的序获得多个锁定时会发生死锁Q。甚x有这U危险,锁定也仅是相对的_粒度协调机Ӟ同样非常适合理单操作,如增加计数器或更C斥拥有者。如果有更细_度的机制来可靠理对单独变量的q发更新Q则会更好一些;在大多数C处理器都有这U机制?/span> 如前所qͼ大多数现代处理器都包含对多处理的支持。当然这U支持包括多处理器可以共享外部设备和d存,同时它通常q包括对指opȝ的增加来支持多处理的Ҏ要求。特别是Q几乎每个现代处理器都有通过可以或L其他处理器的q发讉K的方式来更新׃n变量的指令?/span> 支持q发的第一个处理器提供原子的测试ƈ讄操作Q通常在单位上q行q项操作。现在的处理器(包括 Intel ?Sparc 处理器)使用的最通用的方法是实现名ؓ 比较q{?/span>?CAS 的原语。(?Intel 处理器中Q比较ƈ交换通过指o?cmpxchg pd实现。PowerPC 处理器有一对名?#8220;加蝲q保?#8221;?#8220;条g存储”的指令,它们实现相同的目圎ͼMIPS ?PowerPC 处理器相|除了W一个指令称?#8220;加蝲链接”。) CAS 操作包含三个操作?—?内存位置QVQ、预期原|AQ和新?B)。如果内存位|的g预期原值相匚wQ那么处理器会自动将该位|值更Cؓ新倹{否则,处理器不做Q何操作。无论哪U情况,它都会在 CAS 指o之前q回该位|的倹{(?CAS 的一些特D情况下仅q回 CAS 是否成功Q而不提取当前倹{)CAS 有效地说明了“我认Z|?V 应该包含?AQ如果包含该|则将 B 攑ֈq个位置Q否则,不要更改该位|,只告诉我q个位置现在的值即可?#8221; 通常?CAS 用于同步的方式是从地址 V d?AQ执行多步计来获得新?BQ然后?CAS ?V 的g A 改ؓ B。如?V 处的值尚未同时更改,?CAS 操作成功?/span> cM?CAS 的指令允许算法执行读-修改-写操作,而无需x其他线E同时修改变量,因ؓ如果其他U程修改变量Q那?CAS 会检它Qƈp|Q,法可以对该操作重新计算。清?3 说明?CAS 操作的行为(而不是性能特征Q,但是 CAS 的h值是它可以在g中实玎ͼq且是极轻量U的Q在大多数处理器中)Q?/span>
Z CAS 的ƈ发算法称?无锁?/em>法Q因为线E不必再{待锁定Q有时称Z斥或关键部分Q这取决于线E^台的术语Q。无?CAS 操作成功q是p|Q在M一U情况中Q它都在可预知的旉内完成。如?CAS p|Q调用者可以重?CAS 操作或采取其他适合的操作。清?4 昄了重新编写的计数器类来?CAS 替代锁定Q?/p>
如果每个U程在其他线EQ意gq(或甚臛_败)旉持l进行操作,可以说该算法是 无等?/em>的。与此Ş成对比的是, 无锁?/em>法要求?#160;某个U程L执行操作。(无等待的另一U定义是保证每个U程在其有限的步骤中正确计算自己的操作,而不其他线E的操作、计时、交叉或速度。这一限制可以是系l中U程数的函数Q例如,如果?10 个线E,每个U程都执行一?#160; 再过ȝ 15 q里Qh们已l对无等待且无锁定算法(也称?#160;无阻塞算?/em>Q进行了大量研究Q许多h通用数据l构已经发现了无d法。无d法被广泛用于操作系l和 JVM U别Q进行诸如线E和q程调度{Q务。虽然它们的实现比较复杂Q但相对于基于锁定的备选算法,它们有许多优点:可以避免优先U倒置和死锁等危险Q竞争比较便宜,协调发生在更l的_度U别Q允许更高程度的q行机制{等?/p>
?JDK 5.0 之前Q如果不使用本机代码Q就不能?Java 语言~写无等待、无锁定的算法。在 原子变量cd以认为是 虽然原子变量c表面看h与清?1 中的 调整h竞争的ƈ发应用程序的可~性的通用技术是降低使用的锁定对象的_度Q希望更多的锁定h从竞争变Z竞争。从锁定转换为原子变量可以获得相同的l果Q通过切换为更l粒度的协调机制Q竞争的操作更,从而提高了吞吐量?/p>
无论是直接的q是间接的,几乎 如果没有 JDK 5.0 中的 JVM 改进Q将无法构造这些类Q这些改q暴露了Q向cdQ而不是用LQ接口来讉KgU的同步原语。然后,java.util.concurrent 中的原子变量cd其他cd用户cd开q些功能?/p>
上月Q我介绍?#160; 清单 5 昄了用同步的 PRNG 实现和?CAS 备选实现。注意,要在循环中执?CASQ因为它可能会失败一ơ或多次才能获得成功Q?CAS 的代码Lq样?/p>
下面?1 和图 2 中的图与上月那些囄|只是为基于原子的Ҏ多添加了一行。这些图昄了在 8-way Ultrasparc3 和单处理?Pentium 4 上用不同数量线E的随机发生的吞吐量Q以每秒转数为单位)。测试中的线E数不是真实的;q些U程所表现的竞争比通常多得多,所以它们以比实际程序中低得多的U程数显CZ 大多数用户都不太可能使用原子变量自己开发无d法 ?他们更可能?#160; 开发h员可以直接将原子变量用作׃n计数器、序L成器和其他独立共享变量的高性能替代Q否则必通过同步保护q些变量?/p>
JDK 5.0 是开发高性能q发cȝ巨大q步。通过内部公开新的低协调原语Q和提供一l公共原子变量类Q现在用 Java 语言开发无{待、无锁定法首次变ؓ可行。然后, get()
?increment()
?decrement()
操作。清?1 昄了如何用锁定(同步Q实现该cȝ例子。注意所有方法,甚至需要同?get()
QɾcL为线E安全的c,从而确保没有Q何更C息丢失,所有线E都看到计数器的最新倹{?/p>
清单 1. 同步的计数器c?/strong>
private int value;
public synchronized int getValue() { return value; }
public synchronized int increment() { return ++value; }
public synchronized int decrement() { return --value; }
}increment()
?decrement()
操作是原子的?修改-写操作,Z安全实现计数器,必须使用当前|qؓ其添加一个|或写出新|所有这些均视ؓ一Ҏ作,其他U程不能打断它。否则,如果两个U程试图同时执行增加Q操作的不幸交叉导致计数器只被实现了一ơ,而不是被实现两次。(注意Q通过使值实例变量成为可变变量ƈ不能可靠地完成这Ҏ作。)acquire()
Ҏ也是原子的读-修改-写操作。要获得互斥Q必ȝ保没有其他hh该互斥( curOwner = Thread.currentThread()
Q,然后记录您拥有该互斥的事实( curOwner = Thread.currentThread()
Q,所有这些其他U程不可能在中间出现以及修改 curOwner field
?/p>
private Thread curOwner = null;
public synchronized void acquire() throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
while (curOwner != null)
wait();
curOwner = Thread.currentThread();
}
public synchronized void release() {
if (curOwner == Thread.currentThread()) {
curOwner = null;
notify();
} else
throw new IllegalStateException("not owner of mutex");
}
}ReentrantLock
cd何可以更可~地替代同步Q但是对于一些问题,q有更好的解x法?/span>
private int value;
public synchronized int getValue() { return value; }
public synchronized int compareAndSwap(int expectedValue, int newValue) {
if (value == expectedValue)
value = newValue;
return value;
}
}
private SimulatedCAS value;
public int getValue() {
return value.getValue();
}
public int increment() {
int oldValue = value.getValue();
while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)
oldValue = value.getValue();
return oldValue + 1;
}
}CasCounter.increment()
操作Q最坏的情况下,每个U程必重试最多九ơ,才能完成增加。)java.util.concurrent.atomic
包中d原子变量cM后,q种情况才发生了改变。所有原子变量类都公开比较q设|原语(与比较ƈ交换cMQ,q些原语都是使用q_上可用的最快本机结构(比较q交换、加载链?条g存储Q最坏的情况下是旋{锁)来实现的?#160;java.util.concurrent.atomic
包中提供了原子变量的 9 U风| AtomicInteger
Q?#160;AtomicLong
Q?#160;AtomicReference
Q?#160;AtomicBoolean
Q原子整型;长型Q引用;及原子标记引用和戌引用cȝ数组形式Q其原子地更C对|?/p>
volatile
变量的泛化,它扩展了可变变量的概念,来支持原子条件的比较q设|更新。读取和写入原子变量与读取和写入对可变变量的讉Kh相同的存取语义?/p>
SynchronizedCounter
例子一P但相g是表面的。在表面之下Q原子变量的操作会变为^台提供的用于q发讉K的硬件原语,比如比较q交换?/p>
ABA 问题
因ؓ在更?V 之前QCAS 主要询问“V 的值是否仍?A”Q所以在W一ơ读?V 以及?V 执行 CAS 操作之前Q如果将g A 改ؓ BQ然后再改回 AQ会使基?CAS 的算法乱。在q种情况下,CAS 操作会成功,但是在一些情况下Q结果可能不是您所预期的。(注意Q?#160;清单 1 ?#160;清单 2 中的计数器和互斥例子不存在这个问题,但不是所有算法都q样。)q类问题UCؓ ABA 问题Q通常通过标记或版本~号与要q行 CAS 操作的每个值相兌Qƈ原子地更新值和标记Q来处理q类问题?#160;AtomicStampedReference
cL持这U方法?/td>
java.util.concurrent
包中的所有类都用原子变量,而不使用同步。类?code>ConcurrentLinkedQueue 的类也用原子变量直接实现无{待法Q而类?#160;ConcurrentHashMap
的类使用 ReentrantLock
在需要时q行锁定。然后, ReentrantLock
使用原子变量来维护等待锁定的U程队列?/p>
回页?/strong>
ReentrantLock
如何相对于同步提供可伸羃性优势,以及构造通过伪随机数生成器模拟旋转骰子的单、高竞争CZ基准。我向您昄了通过同步?#160;ReentrantLock
和公q?#160;ReentrantLock
来进行协调的实现Qƈ昄了结果。本月,我将向该基准d其他实现Q?#160;AtomicLong
更新 PRNG 状态的实现?/p>
清单 5. 使用同步和原子变量实现线E安?PRNG
public class PseudoRandomUsingSynch implements PseudoRandom {
private int seed;
public PseudoRandomUsingSynch(int s) { seed = s; }
public synchronized int nextInt(int n) {
int s = seed;
seed = Util.calculateNext(seed);
return s % n;
}
}
public class PseudoRandomUsingAtomic implements PseudoRandom {
private final AtomicInteger seed;
public PseudoRandomUsingAtomic(int s) {
seed = new AtomicInteger(s);
}
public int nextInt(int n) {
for (;;) {
int s = seed.get();
int nexts = Util.calculateNext(s);
if (seed.compareAndSet(s, nexts))
return s % n;
}
}
}
ReentrantLock
与原子变量之间的q。您看刎ͼ虽然 ReentrantLock
拥有比同步更多的优点Q但相对?#160;ReentrantLock
Q原子变量提供了其他改进。(因ؓ在每个工作单元中完成的工作很,所以下囑֏能无法完全地说明?ReentrantLock 相比Q原子变量具有哪些可伸羃性优炏V)
?1. 8-way Ultrasparc3 中同步、ReentrantLock、公q?Lock ?AtomicLong 的基准吞吐量
?2. 单处理器 Pentium 4 中的同步、ReentrantLock、公q?Lock ?AtomicLong 的基准吞吐量
java.util.concurrent
中提供的版本Q如 ConcurrentLinkedQueue
。但是万一您想知道Ҏ以前 JDK 中的相类似的功能Q这些类的性能是如何改q的Q可以用通过原子变量cd开的细_度、硬件别的q发原语?/p>
回页?/strong>
java.util.concurrent
中的cd于这些低U原子变量工h建,为它们提供比以前执行怼功能的类更显著的可~性优炏V虽然您可能永远不会直接使用原子变量Q还是应该ؓ它们的存在而欢呹{?/p>