??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲日韩精品无码专区,亚洲乱码一二三四区麻豆,久久精品亚洲精品国产色婷http://www.tkk7.com/rainmanyang/articles/232766.htmlrainmanrainmanMon, 06 Oct 2008 13:30:00 GMThttp://www.tkk7.com/rainmanyang/articles/232766.htmlhttp://www.tkk7.com/rainmanyang/comments/232766.htmlhttp://www.tkk7.com/rainmanyang/articles/232766.html#Feedback0http://www.tkk7.com/rainmanyang/comments/commentRss/232766.htmlhttp://www.tkk7.com/rainmanyang/services/trackbacks/232766.html阅读全文

rainman 2008-10-06 21:30 发表评论
]]>
ThreadLocal与synchronizehttp://www.tkk7.com/rainmanyang/articles/232633.htmlrainmanrainmanMon, 06 Oct 2008 04:13:00 GMThttp://www.tkk7.com/rainmanyang/articles/232633.htmlhttp://www.tkk7.com/rainmanyang/comments/232633.htmlhttp://www.tkk7.com/rainmanyang/articles/232633.html#Feedback1http://www.tkk7.com/rainmanyang/comments/commentRss/232633.htmlhttp://www.tkk7.com/rainmanyang/services/trackbacks/232633.html阅读全文

rainman 2008-10-06 12:13 发表评论
]]>
再谈ReentrantLockhttp://www.tkk7.com/rainmanyang/articles/232227.htmlrainmanrainmanFri, 03 Oct 2008 09:55:00 GMThttp://www.tkk7.com/rainmanyang/articles/232227.htmlhttp://www.tkk7.com/rainmanyang/comments/232227.htmlhttp://www.tkk7.com/rainmanyang/articles/232227.html#Feedback0http://www.tkk7.com/rainmanyang/comments/commentRss/232227.htmlhttp://www.tkk7.com/rainmanyang/services/trackbacks/232227.html

?/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 Ҏ(gu)和语句所讉K的隐式监视器锁定相同的一些基本行为和语义Q但功能更强大?span class="hilite1" style="background-color: #ffff00; ">ReentrantLock 由最q成功获得锁定,q且q没有释放该锁定的线E所拥有。当锁定没有被另一个线E所拥有Ӟ调用 lock 的线E将成功获取该锁定ƈq回。如果当前线E已l拥有该锁定Q此Ҏ(gu)立卌回。可以?isHeldByCurrentThread() ?getHoldCount() Ҏ(gu)来检查此情况是否发生?/p>

它提供了(jin)lock()Ҏ(gu)Q?br /> 如果该锁定没有被另一个线E保持,则获取该锁定q立卌回,锁定的保持计数讄?1?br /> 如果当前U程已经保持该锁定,则将保持计数?1Qƈ且该Ҏ(gu)立即q回?br /> 如果该锁定被另一个线E保持,则出于线E调度的目的Q禁用当前线E,q且在获得锁定之前,该线E将一直处于休眠状态,此时锁定保持计数被设|ؓ(f) 1?/p>

最q在研究Java concurrent中关于Q务调度的实现ӞM(jin)延迟队列DelayQueue的一些代码,比如take()。该Ҏ(gu)的主要功能是从优先队列(PriorityQueueQ取Z个最应该执行的Q务(最优|(j)Q如果该d的预订执行时间未刎ͼ则需要waitq段旉差。反之,如果旉C(jin)Q则q回该Q务。而offer()Ҏ(gu)是将一个Q务添加到该队列中?/p>

后来产生?jin)一个疑问:(x)如果最应该执行的Q务是一个小时后执行的,而此旉要提交一?0U后执行的Q务,?x)出C么状况?q是先看看take()的源代码Q?/p>

public E take() throws InterruptedException {

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

for (;;) {

E first
= q.peek();

if (first == null) {

available.await();

}
else {

long delay = first.getDelay(TimeUnit.NANOSECONDS);

if (delay > 0) {

long tl = available.awaitNanos(delay);

}
else {

E x
= q.poll();

assert x != null;

if (q.size() != 0)

available.signalAll();
// wake up other takers

return x;

}

}

}

}
finally {

lock.unlock();

}

}

而以下是offer()的源代码:

public boolean offer(E e) {

final ReentrantLock lock = this.lock;

lock.lock();

try {

E first
= q.peek();

q.offer(e);

if (first == null || e.compareTo(first) < 0)

available.signalAll();

return true;

}
finally {

lock.unlock();

}

}

如代码所C,take()和offer()都是lock?jin)重入锁。如果按照synchronized的思维Q用诸如synchronized(obj)的方法)(j)Q这两个Ҏ(gu)是互斥的。回到刚才的疑问Qtake()Ҏ(gu)需要等?个小时才能返回,而offer()需要马上提交一?0U后q行的Q务,?x)不会(x)一直等待take()q回后才能提交呢Q答案是否定的,通过~写验证代码也说明了(jin)q一炏V这让我寚w入锁有了(jin)更大的兴,它确实是一个无d的锁?/p>

下面的代码也许能说明问题Q运行了(jin)4个线E,每一ơ运行前打印l(f)ock的当前状态。运行后都要{待5U钟?/p>

public static void main(String[] args) throws InterruptedException {

final ExecutorService exec = Executors.newFixedThreadPool(4);

final ReentrantLock lock = new ReentrantLock();

final Condition con = lock.newCondition();

final int time = 5;

final Runnable add = new Runnable() {

public void run() {

System.out.println(
"Pre " + lock);

lock.lock();

try {

con.await(time, TimeUnit.SECONDS);

}
catch (InterruptedException e) {

e.printStackTrace();

}
finally {

System.out.println(
"Post " + lock.toString());

lock.unlock();

}

}

};

for(int index = 0; index < 4; index++)

exec.submit(add);

exec.shutdown();

}

 

q是它的输出Q?br /> Pre ReentrantLock@a59698[Unlocked]
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]

每一个线E的锁状态都?#8220;Unlocked”,所以都可以q行。但在把con.awaitҎ(gu)Thread.sleep(5000)Ӟ输出变成了(jin)Q?br /> Pre ReentrantLock@a59698[Unlocked]
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]

以上的对比说明线E在{待?con.await)Q已l不在拥有(keepQ该锁了(jin)Q所以其他线E就可以获得重入锁了(jin)?br />

有必要会(x)q头再看看Java官方的解释:(x)“如果该锁定被另一个线E保持,则出于线E调度的目的Q禁用当前线E,q且在获得锁定之前,该线E将一直处于休眠状?#8221;。我对这里的“保持”的理解是指非wait状态外的所有状态,比如U程Sleep、for循环{一切有CPU参与的活动。一旦线E进入wait状态后Q它?yu)׃再keepq个锁了(jin)Q其他线E就可以获得该锁Q当该线E被唤醒Q触发信h者timeoutQ后Q就接着执行Q会(x)重新“保持”锁,当然前提依然是其他线E已l不?#8220;保持”?jin)该重入锁?/p>

ȝ一句话Q对于重入锁而言Q?lock"?keep"是两个不同的概念。lock?jin)锁Q不一定keep锁,但keep?jin)锁一定已llock?jin)锁?/p>



rainman 2008-10-03 17:55 发表评论
]]>
Java 理论与实? 行的原?/title><link>http://www.tkk7.com/rainmanyang/articles/232204.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Fri, 03 Oct 2008 06:35:00 GMT</pubDate><guid>http://www.tkk7.com/rainmanyang/articles/232204.html</guid><wfw:comment>http://www.tkk7.com/rainmanyang/comments/232204.html</wfw:comment><comments>http://www.tkk7.com/rainmanyang/articles/232204.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/rainmanyang/comments/commentRss/232204.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/rainmanyang/services/trackbacks/232204.html</trackback:ping><description><![CDATA[<span style="font-family: verdana; font-size: 12px; line-height: 19px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">十五q前Q多处理器系l是高度专用pȝQ要p数十万美元(大多数具有两个到四个处理器)(j)。现在,多处理器pȝ很便宜,而且数量很多Q几乎每个主要微处理器都内置?jin)多处理支持Q其中许多系l支持数十个或数百个处理器?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">要用多处理器系l的功能Q通常需要用多U程构造应用程序。但是正如Q何编写ƈ发应用程序的人可以告诉你的那P要获得好的硬件利用率Q只是简单地在多个线E中分割工作是不够的Q还必须保U程实大部分时间都在工作,而不是在{待更多的工作,或等待锁定共享数据结构?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.0"><span id="m4esmmo" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">问题Q线E之间的协调</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果U程之间 <em>?/em>需要协调,那么几乎没有d可以真正地ƈ行。以U程池ؓ(f)例,其中执行的Q务通常怺独立。如果线E池利用公共工作队列Q则从工作队列中删除元素或向工作队列d元素的过E必LU程安全的,q且q意味着要协调对头、尾或节炚w链接指针所q行的访问。正是这U协调导致了(jin)所有问题?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.1"><span id="w42e2gw" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">标准Ҏ(gu)Q锁?/span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">?Java 语言中,协调对共享字D늚讉K的传l方法是使用同步Q确保完成对׃n字段的所有访问,同时h适当的锁定。通过同步Q可以确定(假设cȝ写正)(j)h保护一l给定变量的锁定的所有线E都拥有对q些变量的独占访问权Qƈ且以后其他线E获得该锁定Ӟ可以看到对q些变量q行的更攏V弊端是如果锁定竞争太厉宻IU程常常在其他线E具有锁定时要求获得该锁定)(j)Q会(x)损害吞吐量,因ؓ(f)竞争的同步非常昂c(din)(Public Service AnnouncementQ对于现?JVM 而言Q无竞争的同步现在非怾宜?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Z锁定的算法的另一个问题是Q如果gq具有锁定的U程Q因为页面错误、计划gq或其他意料之外的gq)(j)Q则 <em>没有</em>要求获得该锁定的U程可以l箋q行?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">q可以用可变变量来以比同步更低的成本存储共享变量,但它们有局限性。虽然可以保证其他变量可以立即看到对可变变量的写入,但无法呈现原子操作的?修改-写顺序,q意味着Q比如说Q可变变量无法用来可靠地实现互斥Q互斥锁定)(j)或计数器?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.1"><span id="qgom4c2" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">使用锁定实现计数器和互斥</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">假如开发线E安全的计数器类Q那么这暴?<code>get()</code>?<code>increment()</code> ?<code>decrement()</code> 操作。清?1 昄?jin)如何用锁定(同步Q实现该cȝ例子。注意所有方法,甚至需要同?<code>get()</code>QɾcL为线E安全的c,从而确保没有Q何更C息丢失,所有线E都看到计数器的最新倹{?/p> <br /> <a name="listing1"><strong>清单 1. 同步的计数器c?/strong></a></span> <font face="verdana" size="3"><span style="font-size: 12px; line-height: 19px; "><strong> <div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SynchronizedCounter {<br /> </span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> value;<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value; }<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> increment() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #000000; ">++</span><span style="color: #000000; ">value; }<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> decrement() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #000000; ">--</span><span style="color: #000000; ">value; }<br /> }</span></div> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><font face=" font-weight: normal; "> </font></p> <font face=" font-weight: normal; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><code>increment()</code> ?<code>decrement()</code> 操作是原子的?修改-写操作,Z(jin)安全实现计数器,必须使用当前|qؓ(f)其添加一个|或写出新|所有这些均视ؓ(f)一Ҏ(gu)作,其他U程不能打断它。否则,如果两个U程试图同时执行增加Q操作的不幸交叉导致计数器只被实现?jin)一ơ,而不是被实现两次。(注意Q通过使值实例变量成为可变变量ƈ不能可靠地完成这Ҏ(gu)作。)(j)</p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">许多q发法中都昄?jin)原子的?修改-写组合。清?2 中的代码实现?jin)简单的互斥Q?<code>acquire()</code> Ҏ(gu)也是原子的读-修改-写操作。要获得互斥Q必ȝ保没有其他hh该互斥( <code>curOwner = Thread.currentThread()</code>Q,然后记录(zhn)拥有该互斥的事实( <code>curOwner = Thread.currentThread()</code>Q,所有这些其他U程不可能在中间出现以及(qing)修改 <code>curOwner field</code>?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing2"><strong>清单 2. 同步的互斥类</strong></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong> <div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SynchronizedMutex {<br /> </span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> Thread curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> acquire() </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br /> </span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (Thread.interrupted()) </span><span style="color: #0000FF; ">throw</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> InterruptedException();<br /> </span><span style="color: #0000FF; ">while</span><span style="color: #000000; "> (curOwner </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">) <br /> wait();<br /> curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> Thread.currentThread();<br /> }<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> release() {<br /> </span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (curOwner </span><span style="color: #000000; ">==</span><span style="color: #000000; "> Thread.currentThread()) {<br /> curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br /> notify();<br /> } </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br /> </span><span style="color: #0000FF; ">throw</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> IllegalStateException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">not owner of mutex</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /> }<br /> }</span></div> </strong></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">清单 1 中的计数器类可以可靠地工作,在竞争很或没有竞争旉可以很好地执行。然而,在竞争激烈时Q这大大损x能Q因?JVM 用了(jin)更多的时间来调度U程Q管理竞争和{待U程队列Q而实际工作(如增加计数器Q的旉却很。?zhn)可以回?</span><a style="color: #5c81a7; "><span style="font-weight: normal;">上月专栏</span></a><span style="font-weight: normal;">中的图,该图昄?jin)一旦多个线E用同步竞争一个内|监视器Q吞吐量如何大q度下降。虽然该专栏说明?jin)新?</span><code><span style="font-weight: normal;">ReentrantLock</span></code><span style="font-weight: normal;"> cd何可以更可׾~地替代同步Q但是对于一些问题,q有更好的解x法?/span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.2"><span id="8uyg42y" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-size: 15px; "><span style="font-weight: normal;">锁定问题</span></span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">使用锁定Q如果一个线E试图获取其他线E已l具有的锁定Q那么该U程被dQ直到该锁定可用。此Ҏ(gu)h一些明昄~点Q其中包括当U程被阻塞来{待锁定Ӟ它无法进行其他Q何操作。如果阻塞的U程是高优先U的dQ那么该Ҏ(gu)可能造成非常不好的结果(UCؓ(f) </span><span style="font-weight: normal;">优先U倒置</span><span style="font-weight: normal;">的危险)(j)?/span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">使用锁定q有一些其他危险,如死锁(当以不一致的序获得多个锁定时会(x)发生死锁Q。甚x有这U危险,锁定也仅是相对的_粒度协调机Ӟ同样非常适合理单操作,如增加计数器或更C斥拥有者。如果有更细_度的机制来可靠理对单独变量的q发更新Q则?x)更好一些;在大多数C处理器都有这U机制?/span></p> <strong><br /> </strong></font></strong></span><strong><font face=" font-weight: normal; "></font> <p> </p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><font face=" font-weight: normal; "><strong><br /> </strong> <table border="0" cellspacing="0" cellpadding="0" width="100%"> <tbody> <tr> <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br /> <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br /> <table border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br /> </td> <td valign="top" align="right" style="line-height: 19px; "><a class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> </font></p> <font face=" font-weight: normal; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.0"></a></p> <span style="font-size: 10pt; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.0"><span id="4eks4am" class="atitle" style="font-family: Arial, sans-serif; font-size: 18px; "><span style="font-weight: normal;">g同步原语</span></span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">如前所qͼ大多数现代处理器都包含对多处理的支持。当然这U支持包括多处理器可以共享外部设备和d存,同时它通常q包括对指o(h)pȝ的增加来支持多处理的Ҏ(gu)要求。特别是Q几乎每个现代处理器都有通过可以(g)或L其他处理器的q发讉K的方式来更新׃n变量的指令?/span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.1"><span id="yy4yuui" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-size: 15px; "><span style="font-weight: normal;">比较q交?(CAS)</span></span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">支持q发的第一个处理器提供原子的测试ƈ讄操作Q通常在单位上q行q项操作。现在的处理器(包括 Intel ?Sparc 处理器)(j)使用的最通用的方法是实现名ؓ(f) </span><em><span style="font-weight: normal;">比较q{?/span></em><span style="font-weight: normal;">?CAS 的原语。(?Intel 处理器中Q比较ƈ交换通过指o(h)?cmpxchg pd实现。PowerPC 处理器有一对名?#8220;加蝲q保?#8221;?#8220;条g存储”的指令,它们实现相同的目圎ͼMIPS ?PowerPC 处理器相|除了(jin)W一个指令称?#8220;加蝲链接”。)(j)</span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">CAS 操作包含三个操作?—?内存位置QVQ、预期原|AQ和新?B)。如果内存位|的g预期原值相匚wQ那么处理器?x)自动将该位|值更Cؓ(f)新倹{否则,处理器不做Q何操作。无论哪U情况,它都?x)?CAS 指o(h)之前q回该位|的倹{(?CAS 的一些特D情况下仅q回 CAS 是否成功Q而不提取当前倹{)(j)CAS 有效地说明了(jin)“我认Z|?V 应该包含?AQ如果包含该|则将 B 攑ֈq个位置Q否则,不要更改该位|,只告诉我q个位置现在的值即可?#8221;</span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">通常?CAS 用于同步的方式是从地址 V d?AQ执行多步计来获得新?BQ然后?CAS ?V 的g A 改ؓ(f) B。如?V 处的值尚未同时更改,?CAS 操作成功?/span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">cM?CAS 的指令允许算法执行读-修改-写操作,而无需x其他线E同时修改变量,因ؓ(f)如果其他U程修改变量Q那?CAS ?x)检它Qƈp|Q,法可以对该操作重新计算。清?3 说明?CAS 操作的行为(而不是性能特征Q,但是 CAS 的h(hun)值是它可以在g中实玎ͼq且是极轻量U的Q在大多数处理器中)(j)Q?/span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing3"><span style="font-weight: normal;">清单 3. 说明比较q交换的行ؓ(f)Q而不是性能Q的代码</span></a></p> </span> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing3"><strong></strong></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong> <div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SimulatedCAS {<br /> </span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> value;<br /> <br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value; }<br /> <br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> compareAndSwap(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> expectedValue, </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> newValue) {<br /> </span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (value </span><span style="color: #000000; ">==</span><span style="color: #000000; "> expectedValue) <br /> value </span><span style="color: #000000; ">=</span><span style="color: #000000; "> newValue;<br /> </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value;<br /> }<br /> }</span></div> </strong></p> </font> <p><span style="font-size: 12px; font-weight: normal; line-height: 19px; "> </span></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.2"><span id="6w22mmq" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">?CAS 实现计数?/span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Z CAS 的ƈ发算法称?<em>无锁?/em>法Q因为线E不必再{待锁定Q有时称Z斥或关键部分Q这取决于线E^台的术语Q。无?CAS 操作成功q是p|Q在M一U情况中Q它都在可预知的旉内完成。如?CAS p|Q调用者可以重?CAS 操作或采取其他适合的操作。清?4 昄?jin)重新编写的计数器类来?CAS 替代锁定Q?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> </p> <div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> CasCounter {<br /> </span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> SimulatedCAS value;<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() {<br /> </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value.getValue();<br /> }<br /> </span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> increment() {<br /> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> oldValue </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value.getValue();<br /> </span><span style="color: #0000FF; ">while</span><span style="color: #000000; "> (value.compareAndSwap(oldValue, oldValue </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">) </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> oldValue)<br /> oldValue </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value.getValue();<br /> </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> oldValue </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br /> }<br /> }</span></div> <p> </p> <p><span style="font-size: 12px; font-weight: normal; line-height: 19px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.0"><span id="amcgac6" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">无锁定且无等待算?/span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果每个U程在其他线EQ意gq(或甚臛_败)(j)旉持l进行操作,可以说该算法是 <em>无等?/em>的。与此Ş成对比的是, <em>无锁?/em>法要求?#160;<em>某个</em>U程L执行操作。(无等待的另一U定义是保证每个U程在其有限的步骤中正确计算自己的操作,而不其他线E的操作、计时、交叉或速度。这一限制可以是系l中U程数的函数Q例如,如果?10 个线E,每个U程都执行一?#160;<code>CasCounter.increment()</code> 操作Q最坏的情况下,每个U程必重试最多九(ji)ơ,才能完成增加。)(j)</p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">再过ȝ 15 q里Qh们已l对无等待且无锁定算法(也称?#160;<em>无阻塞算?/em>Q进行了(jin)大量研究Q许多h通用数据l构已经发现?jin)无d法。无d法被广泛用于操作系l和 JVM U别Q进行诸如线E和q程调度{Q务。虽然它们的实现比较复杂Q但相对于基于锁定的备选算法,它们有许多优点:(x)可以避免优先U倒置和死锁等危险Q竞争比较便宜,协调发生在更l的_度U别Q允许更高程度的q行机制{等?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.1"><span id="8qygmmm" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">原子变量c?/span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">?JDK 5.0 之前Q如果不使用本机代码Q就不能?Java 语言~写无等待、无锁定的算法。在 <code>java.util.concurrent.atomic</code> 包中d原子变量cM后,q种情况才发生了(jin)改变。所有原子变量类都公开比较q设|原语(与比较ƈ交换cMQ,q些原语都是使用q_上可用的最快本机结构(比较q交换、加载链?条g存储Q最坏的情况下是旋{锁)(j)来实现的?#160;<code>java.util.concurrent.atomic</code> 包中提供?jin)原子变量?9 U风| <code>AtomicInteger</code>Q?#160;<code>AtomicLong</code>Q?#160;<code>AtomicReference</code>Q?#160;<code>AtomicBoolean</code>Q原子整型;长型Q引用;?qing)原子标记引用和戌引用cȝ数组形式Q其原子地更C对|(j)?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">原子变量cd以认为是 <code>volatile</code> 变量的泛化,它扩展了(jin)可变变量的概念,来支持原子条件的比较q设|更新。读取和写入原子变量与读取和写入对可变变量的讉Kh相同的存取语义?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">虽然原子变量c表面看h与清?1 中的 <code>SynchronizedCounter</code> 例子一P但相g是表面的。在表面之下Q原子变量的操作?x)变为^台提供的用于q发讉K的硬件原语,比如比较q交换?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.2"><span id="y462042" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">更细_度意味着更轻量</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">调整h竞争的ƈ发应用程序的可׾~性的通用技术是降低使用的锁定对象的_度Q希望更多的锁定h从竞争变Z竞争。从锁定转换为原子变量可以获得相同的l果Q通过切换为更l粒度的协调机制Q竞争的操作更,从而提高了(jin)吞吐量?/p> <table align="right" border="0" cellspacing="0" cellpadding="0" width="40%"> <tbody> <tr> <td width="10" style="line-height: 19px; "><img alt="" height="1" width="10" src="http://www.ibm.com/i/c.gif" /></td> <td style="line-height: 19px; "> <table border="1" cellspacing="0" cellpadding="5" width="100%"> <tbody> <tr> <td bgcolor="#eeeeee" style="line-height: 19px; "><a name="sidebar1"><strong>ABA 问题</strong></a><br /> 因ؓ(f)在更?V 之前QCAS 主要询问“V 的值是否仍?A”Q所以在W一ơ读?V 以及(qing)?V 执行 CAS 操作之前Q如果将g A 改ؓ(f) BQ然后再改回 AQ会(x)使基?CAS 的算法؜乱。在q种情况下,CAS 操作?x)成功,但是在一些情况下Q结果可能不是?zhn)所预期的。(注意Q?#160;<a style="color: #5c81a7; ">清单 1</a> ?#160;<a style="color: #5c81a7; ">清单 2</a> 中的计数器和互斥例子不存在这个问题,但不是所有算法都q样。)(j)q类问题UCؓ(f) <em>ABA 问题</em>Q通常通过标记或版本~号与要q行 CAS 操作的每个值相兌Qƈ原子地更新值和标记Q来处理q类问题?#160;<code>AtomicStampedReference</code> cL持这U方法?/td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.3"><span id="i2ycuwm" class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">java.util.concurrent 中的原子变量</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">无论是直接的q是间接的,几乎 <code>java.util.concurrent</code> 包中的所有类都用原子变量,而不使用同步。类?code>ConcurrentLinkedQueue</code> 的类也用原子变量直接实现无{待法Q而类?#160;<code>ConcurrentHashMap</code> 的类使用 <code>ReentrantLock</code> 在需要时q行锁定。然后, <code>ReentrantLock</code> 使用原子变量来维护等待锁定的U程队列?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果没有 JDK 5.0 中的 JVM 改进Q将无法构造这些类Q这些改q暴露了(jin)Q向cdQ而不是用L(fng)Q接口来讉KgU的同步原语。然后,java.util.concurrent 中的原子变量cd其他cd用户cd开q些功能?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%"> <tbody> <tr> <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br /> <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br /> <table border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br /> </td> <td valign="top" align="right" style="line-height: 19px; "><a class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="4.0"><span id="yisoii4" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">使用原子变量获得更高的吞吐量</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a style="color: #5c81a7; ">上月</a>Q我介绍?#160;<code>ReentrantLock</code> 如何相对于同步提供可伸羃性优势,以及(qing)构造通过伪随机数生成器模拟旋转骰子的单、高竞争CZ基准。我向?zhn)昄了(jin)通过同步?#160;<code>ReentrantLock</code> 和公q?#160;<code>ReentrantLock</code> 来进行协调的实现Qƈ昄?jin)结果。本月,我将向该基准d其他实现Q?#160;<code>AtomicLong</code> 更新 PRNG 状态的实现?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">清单 5 昄?jin)用同步?PRNG 实现和?CAS 备选实现。注意,要在循环中执?CASQ因为它可能?x)失败一ơ或多次才能获得成功Q?CAS 的代码Lq样?/p> <br /> <a name="listing5"><strong>清单 5. 使用同步和原子变量实现线E安?PRNG</strong></a><br /> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tbody> <tr> <td class="code-outline" style="line-height: 19px; background-color: #eeeeee; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; "> <pre class="displaycode" style="margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, fixed, monospace; font-size: 11px; "> public class PseudoRandomUsingSynch implements PseudoRandom { private int seed; public PseudoRandomUsingSynch(int s) { seed = s; } public synchronized int nextInt(int n) { int s = seed; seed = Util.calculateNext(seed); return s % n; } } public class PseudoRandomUsingAtomic implements PseudoRandom { private final AtomicInteger seed; public PseudoRandomUsingAtomic(int s) { seed = new AtomicInteger(s); } public int nextInt(int n) { for (;;) { int s = seed.get(); int nexts = Util.calculateNext(s); if (seed.compareAndSet(s, nexts)) return s % n; } } } </pre> </td> </tr> </tbody> </table> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">下面?1 和图 2 中的图与上月那些囄|只是为基于原子的Ҏ(gu)多添加了(jin)一行。这些图昄?jin)?8-way Ultrasparc3 和单处理?Pentium 4 上用不同数量线E的随机发生的吞吐量Q以每秒转数为单位)(j)。测试中的线E数不是真实的;q些U程所表现的竞争比通常多得多,所以它们以比实际程序中低得多的U程数显CZ(jin) <code>ReentrantLock</code> 与原子变量之间的q。?zhn)看刎ͼ虽?#160;<code>ReentrantLock</code> 拥有比同步更多的优点Q但相对?#160;<code>ReentrantLock</code>Q原子变量提供了(jin)其他改进。(因ؓ(f)在每个工作单元中完成的工作很,所以下囑֏能无法完全地说明?ReentrantLock 相比Q原子变量具有哪些可伸羃性优炏V)(j)</p> <br /> <a name="fig1"><strong>?1. 8-way Ultrasparc3 中同步、ReentrantLock、公q?Lock ?AtomicLong 的基准吞吐量</strong></a><br /> <img alt="8-way Ultrasparc3 吞吐? height="332" src="http://www.ibm.com/developerworks/cn/java/j-jtp11234/RngThroughput.gif" width="550" /> <br /> <br /> <a name="fig2"><strong>?2. 单处理器 Pentium 4 中的同步、ReentrantLock、公q?Lock ?AtomicLong 的基准吞吐量</strong></a><br /> <img alt="Uniprocessor Pentium4 吞吐? height="328" src="http://www.ibm.com/developerworks/cn/java/j-jtp11234/RngThroughputUni.gif" width="551" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">大多数用户都不太可能使用原子变量自己开发无d法 ?他们更可能?#160;<code>java.util.concurrent</code> 中提供的版本Q如 <code>ConcurrentLinkedQueue</code>。但是万一(zhn)想知道Ҏ(gu)以前 JDK 中的相类似的功能Q这些类的性能是如何改q的Q可以用通过原子变量cd开的细_度、硬件别的q发原语?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">开发h员可以直接将原子变量用作׃n计数器、序L(fng)成器和其他独立共享变量的高性能替代Q否则必通过同步保护q些变量?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%"> <tbody> <tr> <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br /> <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right"> <tbody> <tr align="right"> <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br /> <table border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br /> </td> <td valign="top" align="right" style="line-height: 19px; "><a class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="5.0"><span id="yygcwuu" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">l束?/span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">JDK 5.0 是开发高性能q发cȝ巨大q步。通过内部公开新的低协调原语Q和提供一l公共原子变量类Q现在用 Java 语言开发无{待、无锁定法首次变ؓ(f)可行。然后, <code>java.util.concurrent</code> 中的cd于这些低U原子变量工h建,为它们提供比以前执行怼功能的类更显著的可׾~性优炏V虽然?zhn)可能永远不?x)直接使用原子变量Q还是应该ؓ(f)它们的存在而欢呹{?/p> </span></p> </strong></font> <img src ="http://www.tkk7.com/rainmanyang/aggbug/232204.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/rainmanyang/" target="_blank">rainman</a> 2008-10-03 14:35 <a href="http://www.tkk7.com/rainmanyang/articles/232204.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现 Java 多线Eƈ发控制框?/title><link>http://www.tkk7.com/rainmanyang/articles/232135.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Thu, 02 Oct 2008 15:12:00 GMT</pubDate><guid>http://www.tkk7.com/rainmanyang/articles/232135.html</guid><wfw:comment>http://www.tkk7.com/rainmanyang/comments/232135.html</wfw:comment><comments>http://www.tkk7.com/rainmanyang/articles/232135.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/rainmanyang/comments/commentRss/232135.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/rainmanyang/services/trackbacks/232135.html</trackback:ping><description><![CDATA[<span style="font-family: verdana; font-size: 12px; line-height: 19px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> </p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N1003A"><span id="kawowma" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">所面(f)的问?/span></a></p> <br /> <a name="N10042"><strong>?1. U程场景</strong></a><br /> <img alt="?1. U程场景" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image001.jpg" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">q幅图中节点代表一?single ThreadQ边代表执行的步骤?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">整幅图代表的意思是QROOT U程执行完毕后执?T1 U程QT1 执行完毕后ƈ发的执行 T2 ?T3。而从 T2 ?T3 指向 T4 的两条边表示的是 T4 必须{?T2 ?T3 都执行完毕以后才能开始执行。剩下的步骤以此cLQ直?END 作ؓ(f)整个q程的结束。当?dng)q只是个略的C意图,可能面对的一个线E场景会(x)有上百个U程。还有,你可以观察到q整个场景只有一个入口点和一个出口点Q这意味着什么?在下文中Z解释?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">q其中涉?qing)到?Java U程的同步互斥机制。例如如何让 T1 ?T2 ?T3 之前q行Q如何让 T2 ?T3 都执行完毕之后开?T4 U程?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> </p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N10059"><span id="42iqiy2" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">模型的描q?/span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如何来描q图 1 中所C的场景呢?可以采用 XML 的格式来描述我们的模型。我定义一?#8220;Thread” element 来表C线E?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> </p> <div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #000000; "><</span><span style="color: #000000; ">ThreadList</span><span style="color: #000000; ">></span><span style="color: #000000; "><br /> </span><span style="color: #000000; "><</span><span style="color: #000000; ">Thread ID </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">"</span><span style="color: #000000; ">thread-id</span><span style="color: #000000; ">"</span><span style="color: #000000; "> PRETHREAD </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">"</span><span style="color: #000000; ">prethread1, prethread2…</span><span style="color: #000000; ">"</span><span style="color: #000000; ">></</span><span style="color: #000000; ">Thread</span><span style="color: #000000; ">></span><span style="color: #000000; "><br /> </span><span style="color: #000000; "><</span><span style="color: #000000; ">Thread ID </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">"</span><span style="color: #000000; ">thread-id</span><span style="color: #000000; ">"</span><span style="color: #000000; "> PRETHREAD </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">"</span><span style="color: #000000; ">prethread3, prethread4…</span><span style="color: #000000; ">"</span><span style="color: #000000; ">></</span><span style="color: #000000; ">Thread</span><span style="color: #000000; ">></span><span style="color: #000000; "><br /> </span><span style="color: #000000; "></</span><span style="color: #000000; ">ThreadList</span><span style="color: #000000; ">></span></div> <p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">其中 ID 是线E的唯一标识W,PRETHREAD 便是该线E的直接先决U程的IDQ每个线E?ID 之间用逗号隔开?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">?Thread q个 element 里面可以加入你想要该U程执行d的具体信息?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">实际上模型的描述是解决问题非帔R要的一个环节,整个U程场景可以用一U一致的形式来描qͼ作ؓ(f) Java 多线Eƈ发控制框架引擎的输入。也是线E运行的模式?XML 来描q出来,q样只用改动 XML 配置文g可以更Ҏ(gu)个线E运行的模式Q不用改动Q何的源代码?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N1006F" style="color: #5c81a7; "><span id="44yciw2" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">两种实现机制</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">对于 Java 多线E的q行框架来说Q我们将采用“?#8221;?#8220;?#8221;的两U模式来实现?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><br /> </p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N10078"><span id="qeic2wi" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">“?#8221; - ȝE轮?/span></a></p> <br /> <a name="N10080"><strong>?2. 静态类?/strong></a><br /> <img alt="?2. 静态类? border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image002.gif" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Thread 是工作线E。ThreadEntry ?Thread 的包装类Qprerequisite 是一?HashMapQ它含有 Thread 的先决线E的状态。如?中显C的那样QT4 的先决线E是 T2 ?T3Q那?prerequisite 中就包含 T2 ?T3 的状态。TestScenario 中的 threadEntryList 中包含所有的 ThreadEntry?/p> <br /> <a name="N10093"><strong>?3. U程执行场景</strong></a><br /> <img alt="?3. U程执行场景" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image003.gif" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">TestScenario 作ؓ(f)ȝE,作ؓ(f)一?#8220;?#8221;在的监控者,不断地轮?threadEntryList 中所?ThreadEntry 的状态,?ThreadEntry 接受?isReady 的查询后查询自己?prerequisiteQ当其中所有的先决U程的状态ؓ(f)“正常l束?#8221;Q它便返?readyQ那?TestScenario 便会(x)调用 ThreadEntry ?startThread() Ҏ(gu)授权?ThreadEntry q行U程QThread 侉K过 run() Ҏ(gu)来真正执行线E。ƈ在正常执行完毕后调用 setPreRequisteState() Ҏ(gu)来更新整?ScenarioQthreadEntryList 中所?ThreadEntry ?prerequisite 里面含有?Thread 的状态信息ؓ(f)“正常l束”?/p> <br /> <a name="N100A6"><strong>?4. 状态更改的q程</strong></a><br /> <img alt="?4. 状态更改的q程" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image004.jpg" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如图 1 中所C的 T4 的先决线Eؓ(f) T2 ?T3QT2 ?T3 q行执行。如?4 所C,假设 T2 先执行完毕,它会(x)调用 setPreRequisteState() Ҏ(gu)来更新整?ScenarioQ?threadEntryList 中所?ThreadEntry ?prerequisite 里面含有?T2 的状态信息ؓ(f)“正常l束”。此ӞT4 ?prerequisite ?T2 的状态ؓ(f)“正常l束”Q但?T3 q没有执行完毕,所以其状态ؓ(f)“未完?#8221;。所?T4 ?isReady 查询q回?falseQT4 不会(x)执行。只有当 T3 执行完毕后更新状态ؓ(f)“正常l束”后,T4 的状态才?readyQT4 才会(x)开始运行?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">其余的节点也以此cLQ它们正常执行完毕的时候会(x)在整个的 scenario 中广播该U程正常l束的信息,׃U程不断地轮询各?ThreadEntry 的状态来开启各个线E?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">q便是采用主控线E轮询状态表的方式来控制 Java 多线E运行框架的实现方式之一?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>优点Q?/strong>概念l构清晰明了(jin)Q实现简单。避免采?Java 的锁机制Q减生死锁的几率。当发生异常D其中某些U程不能正常执行完毕的时候,不会(x)产生挂v的线E?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>~点Q?/strong>采用ȝE轮询机Ӟ耗费 CPU 旉。当图中的节点太多的(n>??? 而线E单个线E执行时间比较短的时?t<??? 需要进一步研I?时候会(x)产生U程启动的些微gq,也就是说实时性能在极端情况下不好Q当然这可以另外写一文章来专门探讨?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><br /> </p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N100C7"><span id="oaqy2m2" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">“?#8221; - wait&notify</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">相对?#8220;?#8221;-ȝE轮询机制来_(d)“?#8221;采用的是自我控制q锁触发机制?/p> <br /> <a name="N100D2"><strong>?5. 锁机制的静态类?/strong></a><br /> <img alt="?5. 锁机制的静态类? border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image005.gif" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Thread 中的 lock 为当?Thread ?lockQlockList 是一?HashMapQ持有其后U程?lock 的引用,getLock ?setLock 可以?lockList 中的 Lock q行操作。其中很重要的一个成员是 waitForCountQ这是一个引用计数。表明当前线E正在等待的先决U程的个敎ͼ例如?1 中所C的 T4Q在初始的情况下Q他{待的先决线E是 T2 ?T3Q那么它?waitForCount {于 2?/p> <br /> <a name="N100E5"><strong>?6. 锁机制执行顺序图</strong></a><br /> <img alt="?6. 锁机制执行顺序图" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image006.gif" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">当整个过E开始运行的时候,我们所有的U程 startQ但是每个线E所持的 lock 都处?wait 状态,U程都会(x)处于 waiting 的状态。此Ӟ我们?root thread 所持有的自w的 lock notifyQ这?root thread ׃(x)q行h。当 root ?run Ҏ(gu)执行完毕以后。它?x)检查其后箋U程?waitForCountQƈ其值减一。然后再ơ检?waitForCountQ如?waitForCount {于 0Q表C后箋U程的所有先决线E都已经执行完毕Q此时我?notify 该线E的 lockQ该后箋U程便可以从 waiting 的状态{换成?running 的状态。然后这个过E连锁递归的进行下去,整个q程便会(x)执行完毕?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">我们q是?T2QT3QT4 ZQ当q行 initThreadLock q程的时候,我们可以知道 T4 有两个直接先决线E?T2 ?T3Q所?T4 ?waitForCount {于 2。我们假?T3 先执行完毕,T2 仍然?running 的状态,此时他会(x)首先遍历其所有的直接后U程Qƈ他们的 waitForCount 减去 1Q此时他只有一个直接后l线E?T4Q于?T4 ?waitForCount 减去 1 以后值变?1Q不{于 0Q此时不?x)?T4 ?lock notifyQT4 l箋 waiting。当 T2 执行完毕之后Q他?x)执行?T3 相同的步骤,此时 T4 ?waitForCount {于 0QT2 ?notify T4 ?lockQ于?T4 ?waiting 状态{换成?running 状态。其他的节点也是怼的情c(din)?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">当然Q我们也可以整个过E的信息攑֜另外的一个全局对象中,所有的U程都去查找该全局对象来获取各自所需的信息,而不是采取这U分布式存储的方式?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>优点Q?/strong>采用 wait&notify 机制而不采用轮询的机Ӟ不会(x)费CPU资源。执行效率较高。而且相对?#8220;?#8221;-ȝE轮询的机制来说实时性更好?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>~点Q?/strong>采用 Java U程 Object 的锁机制Q实现v来较为复杂。而且采取一U连锁触发的方式Q如果其中某些线E异常,?x)导致所有其后U程的挂赯(g)造成整个 scenario 的运行失败。ؓ(f)?jin)防止这U情늚发生Q我们还必须建立一套线E监控的机制来确保其正常q行?/p> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N10106"><span id="6622m2k" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">延</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">下面的图所要表辄是这样一U递归q代的概c(din)例如在? 中展C的那样QT1 q个节点表示的是一个线E。现在,忘掉U程q样一个概念,?T1 抽象Z个过E,惌它是一个银河系Q深入到 T1 中去Q它也是一个许多子q程的集合,q些子过E之间的关系模式如?1 所C那P可以用一个图来表C?/p> <br /> <a name="N10111"><strong>?7. 嵌套子过E?/strong></a><br /> <img alt="?7. 嵌套子过E? border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image007.jpg" /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">可以惌一下这是怎样的一个框Ӟh无穷扩展性的q程框架Q我们只用定义各个过E之间的关系Q我们不用关?j)过E是怎样q行的。事实上Q可以在最l的节点上指定一个实际的工作Q比如读一个文Ӟ或者submit一个JCL jobQ或者执行一条sql statement?/p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">其实Q按照某U遍历规则,完全可以这U嵌套递归的结构{化成Z个一层扁q结构的图,而不是原来的分层的网状结构,但是我们不这样做的原因是Z以下的几点考虑Q?/p> <ol style="margin-top: 2px; margin-bottom: 2px; padding-top: 2px; padding-bottom: 2px; "> <li>如果q样做,?x)导致图节点太多Q边太多Qo(h)人眼q݋乱?/li> <li>不这样做更主要的原因是每一个场景,如图 7 中的 T1QT13Q是状态聚集的一个单元,h高复用性和可靠性?/li> <li>框架是高度抽象的Q它实际的执行可以是分布式的Q一个单元可以是一个系l,作ؓ(f)和其他系l的分界标志?/li> </ol> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">实际上,q是一个状态聚集的层次控制框架Q我们可以依赖此框架来执行自主运。我们将在其它的文章中来讨论它的应用?/p> <br /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N10134"><span id="2u2m4si" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">ȝ</span></a></p> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">本文介绍?jin)一U?Java 多线Eƈ发控制的框架Qƈl出?jin)其两种实现的模型,它们有各自的优缺点,有各自的适用范围。当需要进?Java U程的ƈ发控制的时候,可以作ؓ(f)参考?/p> <br /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="resources"><span id="iumuamy" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">参考资?/span></a></p> <ul style="margin-top: 2px; margin-bottom: 2px; padding-top: 2px; padding-bottom: 2px; "> <li>developerWorks Java 专区 Peter Haggar 的文章:(x)<a target="_blank" style="color: #5c81a7; ">Apply the Specific Notification pattern to control the order of thread execution</a><br /> <br /> </li> <li>Doug Lea 的著名ƈ发性图书:(x)<a target="_blank" style="color: #5c81a7; "><em>Java q发~程: 设计原则与模? W二?Addison Wesley 1999)</em></a><br /> <br /> </li> <li>另一本关于ƈ发性的图书Q?a style="color: #5c81a7; "><em>Java Concurrency in Practice</em></a><br /> <br /> </li> <li>developerWorks Java 专区 Joseph HartalQZe'ev Bubis 的文章:(x)<a style="color: #996699; ">使你L得进行多U程应用E序~程</a><br /> <br /> </li> <li>developerWorks Java 专区 Alex Roetter 的文章:(x)<a style="color: #5c81a7; ">~写多线E的Java应用E序</a><br /> <br /> </li> <li>developerWorks Java 专区 Neel V. Kumar 的文章:(x)<a style="color: #996699; ">Java E序中的多线E?/a></li> </ul> <br /> <br /> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="author"><span id="qqyekya" class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">关于作?/span></a></p> <table border="0" cellspacing="0" cellpadding="0" width="100%"> <tbody> <tr> <td colspan="3" style="line-height: 19px; "><img alt="" width="100%" height="5" src="http://www.ibm.com/i/c.gif" /></td> </tr> <tr align="left" valign="top"> <td style="line-height: 19px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "></p> </td> <td style="line-height: 19px; "><img alt="" width="4" height="5" src="http://www.ibm.com/i/c.gif" /></td> <td width="100%" style="line-height: 19px; "> <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">陈威Q华中科技大学士QIBM CSDL Software EngineerQ所在的 Team ?DB2 for z/OS。联pL式:(x)chenwbj@cn.ibm.com</p> <div><br /> </div> </td> </tr> </tbody> </table> </p> </p> </p> </p> <p> </p> <p> </p> </span> <img src ="http://www.tkk7.com/rainmanyang/aggbug/232135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/rainmanyang/" target="_blank">rainman</a> 2008-10-02 23:12 <a href="http://www.tkk7.com/rainmanyang/articles/232135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Synchronization and the Java Memory Modelhttp://www.tkk7.com/rainmanyang/articles/231899.htmlrainmanrainmanTue, 30 Sep 2008 08:55:00 GMThttp://www.tkk7.com/rainmanyang/articles/231899.htmlhttp://www.tkk7.com/rainmanyang/comments/231899.htmlhttp://www.tkk7.com/rainmanyang/articles/231899.html#Feedback0http://www.tkk7.com/rainmanyang/comments/commentRss/231899.htmlhttp://www.tkk7.com/rainmanyang/services/trackbacks/231899.htmlThis set of excerpts from section 2.2 includes the main discussions on how the Java Memory Model impacts concurrent programming.

For information about ongoing work on the memory model, see Bill Pugh's Java Memory Model pages.

 

Consider the tiny class, defined without any synchronization:

final class SetCheck {
private int  a = 0;
private long b = 0;
void set() {
a =  1;
b = -1;
}
boolean check() {
return ((b ==  0) ||
(b == -1 && a == 1));
}
}
In a purely sequential language, the method check could never return false. This holds even though compilers, run-time systems, and hardware might process this code in a way that you might not intuitively expect. For example, any of the following might apply to the execution of method set:
  • The compiler may rearrange the order of the statements, so b may be assigned before a. If the method is inlined, the compiler may further rearrange the orders with respect to yet other statements.
  • The processor may rearrange the execution order of machine instructions corresponding to the statements, or even execute them at the same time.

     

  • The memory system (as governed by cache control units) may rearrange the order in which writes are committed to memory cells corresponding to the variables. These writes may overlap with other computations and memory actions.

     

  • The compiler, processor, and/or memory system may interleave the machine-level effects of the two statements. For example on a 32-bit machine, the high-order word of b may be written first, followed by the write to a, followed by the write to the low-order word of b.

     

  • The compiler, processor, and/or memory system may cause the memory cells representing the variables not to be updated until sometime after (if ever) a subsequent check is called, but instead to maintain the corresponding values (for example in CPU registers) in such a way that the code still has the intended effect.
In a sequential language, none of this can matter so long as program execution obeys as-if-serial semantics. Sequential programs cannot depend on the internal processing details of statements within simple code blocks, so they are free to be manipulated in all these ways. This provides essential flexibility for compilers and machines. Exploitation of such opportunities (via pipelined superscalar CPUs, multilevel caches, load/store balancing, interprocedural register allocation, and so on) is responsible for a significant amount of the massive improvements in execution speed seen in computing over the past decade. The as-if-serial property of these manipulations shields sequential programmers from needing to know if or how they take place. Programmers who never create their own threads are almost never impacted by these issues.

Things are different in concurrent programming. Here, it is entirely possible for check to be called in one thread while set is being executed in another, in which case the check might be "spying" on the optimized execution of set. And if any of the above manipulations occur, it is possible for check to return false. For example, as detailed below, check could read a value for the long b that is neither 0 nor -1, but instead a half-written in-between value. Also, out-of-order execution of the statements in set may cause check to read b as -1 but then read a as still 0.

In other words, not only may concurrent executions be interleaved, but they may also be reordered and otherwise manipulated in an optimized form that bears little resemblance to their source code. As compiler and run-time technology matures and multiprocessors become more prevalent, such phenomena become more common. They can lead to surprising results for programmers with backgrounds in sequential programming (in other words, just about all programmers) who have never been exposed to the underlying execution properties of allegedly sequential code. This can be the source of subtle concurrent programming errors.

In almost all cases, there is an obvious, simple way to avoid contemplation of all the complexities arising in concurrent programs due to optimized execution mechanics: Use synchronization. For example, if both methods in class SetCheck are declared as synchronized, then you can be sure that no internal processing details can affect the intended outcome of this code.

But sometimes you cannot or do not want to use synchronization. Or perhaps you must reason about someone else's code that does not use it. In these cases you must rely on the minimal guarantees about resulting semantics spelled out by the Java Memory Model. This model allows the kinds of manipulations listed above, but bounds their potential effects on execution semantics and additionally points to some techniques programmers can use to control some aspects of these semantics (most of which are discussed in K?).

The Java Memory Model is part of The JavaTM Language Specification, described primarily in JLS chapter 17. Here, we discuss only the basic motivation, properties, and programming consequences of the model. The treatment here reflects a few clarifications and updates that are missing from the first edition of JLS.

The assumptions underlying the model can be viewed as an idealization of a standard SMP machine of the sort described in K?.4:

For purposes of the model, every thread can be thought of as running on a different CPU from any other thread. Even on multiprocessors, this is infrequent in practice, but the fact that this CPU-per-thread mapping is among the legal ways to implement threads accounts for some of the model's initially surprising properties. For example, because CPUs hold registers that cannot be directly accessed by other CPUs, the model must allow for cases in which one thread does not know about values being manipulated by another thread. However, the impact of the model is by no means restricted to multiprocessors. The actions of compilers and processors can lead to identical concerns even on single-CPU systems.

The model does not specifically address whether the kinds of execution tactics discussed above are performed by compilers, CPUs, cache controllers, or any other mechanism. It does not even discuss them in terms of classes, objects, and methods familiar to programmers. Instead, the model defines an abstract relation between threads and main memory. Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. The model guarantees a few properties surrounding the interactions of instruction sequences corresponding to methods and memory cells corresponding to fields. Most rules are phrased in terms of when values must be transferred between the main memory and per-thread working memory. The rules address three intertwined issues:

Atomicity
Which instructions must have indivisible effects. For purposes of the model, these rules need to be stated only for simple reads and writes of memory cells representing fields - instance and static variables, also including array elements, but not including local variables inside methods.
Visibility
Under what conditions the effects of one thread are visible to another. The effects of interest here are writes to fields, as seen via reads of those fields.
Ordering
Under what conditions the effects of operations can appear out of order to any given thread. The main ordering issues surround reads and writes associated with sequences of assignment statements.
When synchronization is used consistently, each of these properties has a simple characterization: All changes made in one synchronized method or block are atomic and visible with respect to other synchronized methods and blocks employing the same lock, and processing of synchronized methods or blocks within any given thread is in program-specified order. Even though processing of statements within blocks may be out of order, this cannot matter to other threads employing synchronization.

When synchronization is not used or is used inconsistently, answers become more complex. The guarantees made by the memory model are weaker than most programmers intuitively expect, and are also weaker than those typically provided on any given JVM implementation. This imposes additional obligations on programmers attempting to ensure the object consistency relations that lie at the heart of exclusion practices: Objects must maintain invariants as seen by all threads that rely on them, not just by the thread performing any given state modification.

The most important rules and properties specified by the model are discussed below.

 

Atomicity

Accesses and updates to the memory cells corresponding to fields of any type except long or double are guaranteed to be atomic. This includes fields serving as references to other objects. Additionally, atomicity extends to volatile long and double. (Even though non-volatile longs and doubles are not guaranteed atomic, they are of course allowed to be.)

Atomicity guarantees ensure that when a non-long/double field is used in an expression, you will obtain either its initial value or some value that was written by some thread, but not some jumble of bits resulting from two or more threads both trying to write values at the same time. However, as seen below, atomicity alone does not guarantee that you will get the value most recently written by any thread. For this reason, atomicity guarantees per se normally have little impact on concurrent program design.

 

Visibility

Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions:
  • A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.

    In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields. While lock actions provide exclusion only for the operations performed within a synchronized method or block, these memory effects are defined to cover all fields used by the thread performing the action.

    Note the double meaning of synchronized: it deals with locks that permit higher-level synchronization protocols, while at the same time dealing with the memory system (sometimes via low-level memory barrier machine instructions) to keep value representations in synch across threads. This reflects one way in which concurrent programming bears more similarity to distributed programming than to sequential programming. The latter sense of synchronized may be viewed as a mechanism by which a method running in one thread indicates that it is willing to send and/or receive changes to variables to and from methods running in other threads. From this point of view, using locks and passing messages might be seen merely as syntactic variants of each other.

     

  • If a field is declared as volatile, any value written to it is flushed and made visible by the writer thread before the writer thread performs any further memory operation (i.e., for the purposes at hand it is flushed immediately). Reader threads must reload the values of volatile fields upon each access.

     

  • The first time a thread accesses a field of an object, it sees either the initial value of the field or a value since written by some other thread.

    Among other consequences, it is bad practice to make available the reference to an incompletely constructed object (see K?.2). It can also be risky to start new threads inside a constructor, especially in a class that may be subclassed. Thread.start has the same memory effects as a lock release by the thread calling start, followed by a lock acquire by the started thread. If a Runnable superclass invokes new Thread(this).start() before subclass constructors execute, then the object might not be fully initialized when the run method executes. Similarly, if you create and start a new thread T and then create an object X used by thread T, you cannot be sure that the fields of X will be visible to T unless you employ synchronization surrounding all references to object X. Or, when applicable, you can create X before starting T.

     

  • As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the effects made by that thread (see K?.2).
Note that visibility problems never arise when passing references to objects across methods in the same thread.

The memory model guarantees that, given the eventual occurrence of the above operations, a particular update to a particular field made by one thread will eventually be visible to another. But eventually can be an arbitrarily long time. Long stretches of code in threads that use no synchronization can be hopelessly out of synch with other threads with respect to values of fields. In particular, it is always wrong to write loops waiting for values written by other threads unless the fields are volatile or accessed via synchronization (see K?.6).

The model also allows inconsistent visibility in the absence of synchronization. For example, it is possible to obtain a fresh value for one field of an object, but a stale value for another. Similarly, it is possible to read a fresh, updated value of a reference variable, but a stale value of one of the fields of the object now being referenced.

However, the rules do not require visibility failures across threads, they merely allow these failures to occur. This is one aspect of the fact that not using synchronization in multithreaded code doesn't guarantee safety violations, it just allows them. On most current JVM implementations and platforms, even those employing multiple processors, detectable visibility failures rarely occur. The use of common caches across threads sharing a CPU, the lack of aggressive compiler-based optimizations, and the presence of strong cache consistency hardware often cause values to act as if they propagate immediately among threads. This makes testing for freedom from visibility-based errors impractical, since such errors might occur extremely rarely, or only on platforms you do not have access to, or only on those that have not even been built yet. These same comments apply to multithreaded safety failures more generally. Concurrent programs that do not use synchronization fail for many reasons, including memory consistency problems.

 

Ordering

Ordering rules fall under two cases, within-thread and between-thread:
  • From the point of view of the thread performing the actions in a method, instructions proceed in the normal as-if-serial manner that applies in sequential programming languages.

     

  • From the point of view of other threads that might be "spying" on this thread by concurrently running unsynchronized methods, almost anything can happen. The only useful constraint is that the relative orderings of synchronized methods and blocks, as well as operations on volatile fields, are always preserved.
Again, these are only the minimal guaranteed properties. In any given program or platform, you may find stricter orderings. But you cannot rely on them, and you may find it difficult to test for code that would fail on JVM implementations that have different properties but still conform to the rules.

Note that the within-thread point of view is implicitly adopted in all other discussions of semantics in JLS. For example, arithmetic expression evaluation is performed in left-to-right order (JLS section 15.6) as viewed by the thread performing the operations, but not necessarily as viewed by other threads.

The within-thread as-if-serial property is helpful only when only one thread at a time is manipulating variables, due to synchronization, structural exclusion, or pure chance. When multiple threads are all running unsynchronized code that reads and writes common fields, then arbitrary interleavings, atomicity failures, race conditions, and visibility failures may result in execution patterns that make the notion of as-if-serial just about meaningless with respect to any given thread.

Even though JLS addresses some particular legal and illegal reorderings that can occur, interactions with these other issues reduce practical guarantees to saying that the results may reflect just about any possible interleaving of just about any possible reordering. So there is no point in trying to reason about the ordering properties of such code.

 

Volatile

In terms of atomicity, visibility, and ordering, declaring a field as volatile is nearly identical in effect to using a little fully synchronized class protecting only that field via get/set methods, as in:
final class VFloat {
private float value;
final synchronized void  set(float f) { value = f; }
final synchronized float get()        { return value; }
}
Declaring a field as volatile differs only in that no locking is involved. In particular, composite read/write operations such as the "++'' operation on volatile variables are not performed atomically.

Also, ordering and visibility effects surround only the single access or update to the volatile field itself. Declaring a reference field as volatile does not ensure visibility of non-volatile fields that are accessed via this reference. Similarly, declaring an array field as volatile does not ensure visibility of its elements. Volatility cannot be manually propagated for arrays because array elements themselves cannot be declared as volatile.

Because no locking is involved, declaring fields as volatile is likely to be cheaper than using synchronization, or at least no more expensive. However, if volatile fields are accessed frequently inside methods, their use is likely to lead to slower performance than would locking the entire methods.

Declaring fields as volatile can be useful when you do not need locking for any other reason, yet values must be accurately accessible across multiple threads. This may occur when:

 

  • The field need not obey any invariants with respect to others.

     

  • Writes to the field do not depend on its current value.

     

  • No thread ever writes an illegal value with respect to intended semantics.
  • The actions of readers do not depend on values of other non-volatile fields.
Using volatile fields can make sense when it is somehow known that only one thread can change a field, but many other threads are allowed to read it at any time. For example, a Thermometer class might declare its temperature field as volatile. As discussed in K?.2, a volatile can be useful as a completion flag. Additional examples are illustrated in K?, where the use of lightweight executable frameworks automates some aspects of synchronization, but volatile declarations are needed to ensure that result field values are visible across tasks.

rainman 2008-09-30 16:55 发表评论
]]>
L使用U程: 同步不是敌h(转蝲自ibm developwork)http://www.tkk7.com/rainmanyang/articles/231868.htmlrainmanrainmanTue, 30 Sep 2008 05:31:00 GMThttp://www.tkk7.com/rainmanyang/articles/231868.htmlhttp://www.tkk7.com/rainmanyang/comments/231868.htmlhttp://www.tkk7.com/rainmanyang/articles/231868.html#Feedback0http://www.tkk7.com/rainmanyang/comments/commentRss/231868.htmlhttp://www.tkk7.com/rainmanyang/services/trackbacks/231868.html

大多数编E语a的语a规范都不?x)谈到线E和q发的问题;因ؓ(f)一直以来,q些问题都是留给q_或操作系l去详细说明的。但是,Java 语言规范QJLSQ却明确包括一个线E模型,q提供了(jin)一些语a元素供开发h员用以保证他们E序的线E安全?/p>

对线E的明确支持有利也有弊。它使得我们在写E序时更Ҏ(gu)利用U程的功能和便利Q但同时也意味着我们不得不注意所写类的线E安全,因ؓ(f)Mc都很有可能被用在一个多U程的环境内?/p>

许多用户W一ơ发C们不得不ȝ解线E的概念的时候,q不是因Z们在写创建和理U程的程序,而是因ؓ(f)他们正在用一个本w是多线E的工具或框架。Q何用q?Swing GUI 框架或写q小服务E序?JSP 늚开发h员(不管有没有意识到Q都曄被线E的复杂性困扰过?/p>

Java 设计师是惛_ZU语aQ之能够很好地q行在现代的gQ包括多处理器系l上。要辑ֈq一目的Q管理线E间协调的工作主要推l了(jin)软g开发h员;E序员必L定线E间׃n数据的位|。在 Java E序中,用来理U程间协调工作的主要工具?synchronized 关键字。在~少同步的情况下QJVM 可以很自由地对不同线E内执行的操作进行计时和排序。在大部分情况下Q这正是我们惌的,因ؓ(f)q样可以提高性能Q但它也l程序员带来?jin)额外的负担Q他们不得不自己识别什么时候这U性能的提高会(x)危及(qing)E序的正性?/p>

synchronized 真正意味着什么?

大部?Java E序员对同步的块或方法的理解是完全根据用互斥(互斥信号量)(j)或定义一个(f)界段Q一个必d子性地执行的代码块Q。虽?synchronized 的语义中实包括互斥和原子性,但在程q入之前和在程退Z后发生的事情要复杂得多?/p>

synchronized 的语义确实保证了(jin)一ơ只有一个线E可以访问被保护的区D,但同时还包括同步U程在主存内互相作用的规则。理?Java 内存模型QJMMQ的一个好Ҏ(gu)是把各个线E想像成q行在相互分ȝ处理器上Q所有的处理器存取同一块主存空_(d)每个处理器有自己的缓存,但这些缓存可能ƈ不dd同步。在~少同步的情况下QJMM ?x)允怸个线E在同一个内存地址上看C同的倹{而当用一个管E(锁)(j)q行同步的时候,一旦申请加?jin)锁QJMM ׃(x)马上要求该缓存失效,然后在它被释攑։对它q行hQ把修改q的内存位置写回dQ。不隄Zؓ(f)什么同步会(x)对程序的性能影响q么大;频繁地刷新缓存代价会(x)很大?/p>






回页?/strong>


使用一条好的运行\U?/span>

如果同步不适当Q后果是很严重的Q会(x)造成数据混ؕ和争用情况,DE序崩溃Q生不正确的结果,或者是不可预计的运行。更p的是,q些情况可能很少发生且具有偶然性(使得问题很难被监和重现Q。如果测试环境和开发环境有很大的不同,无论是配|的不同Q还是负L(fng)不同Q都有可能得这些问题在试环境中根本不出现Q从而得出错误的l论Q我们的E序是正的Q而事实上q些问题只是q没出现而已?/p>
争用情况定义

争用情况是一U特定的情况Q两个或更多的线E或q程L写一些共享数据,而最l结果取决于q些U程是如何被调度计时的。争用情况可能会(x)D不可预见的结果和隐蔽的程序错误?/p>

另一斚wQ不当或q度C用同步会(x)D其它问题Q比如性能很差和死锁。当?dng)性能差虽然不如数据؜乱那么严重,但也是一个严重的问题Q因此同样不可忽视。编写优U的多U程E序需要用好的运行\U,_的同步可以(zhn)的数据不发生؜乱,但不需要滥用到L担死锁或不必要地削弱E序性能的风险?/p>



回页?/strong>


同步的代h多大Q?/span>

׃包括~存h和设|失效的q程QJava 语言中的同步块通常比许多^台提供的临界D设备代h大,q些临界D通常是用一个原子性的“test and set bit”机器指o(h)实现的。即使一个程序只包括一个在单一处理器上q行的单U程Q一个同步的Ҏ(gu)调用仍要比非同步的方法调用慢。如果同步时q发生锁定争用,那么性能上付出的代h(hun)?x)大得多Q因Z(x)需要几个线E切换和pȝ调用?/p>

q运的是Q随着每一版的 JVM 的不断改q,既提高了(jin) Java E序的M性能Q同时也相对减少?jin)同步的代h(hun)Qƈ且将来还可能?x)有q一步的改进。此外,同步的性能代h(hun)l常是被夸大的。一个著名的资料来源曾l引证说一个同步的Ҏ(gu)调用比一个非同步的方法调用慢 50 倍。虽然这句话有可能是真的Q但也会(x)产生误导Q而且已经D?jin)许多开发h员即使在需要的时候也避免使用同步?/p>

严格依照癑ֈ比计同步的性能损失q没有多大意义,因ؓ(f)一个无争用的同步给一个块或方法带来的是固定的性能损失。而这一固定的gq带来的性能损失癑ֈ比取决于在该同步块内做了(jin)多少工作。对一?em>I?/em>Ҏ(gu)的同步调用可能要比对一个空Ҏ(gu)的非同步调用?20 倍,但我们多长时间才调用一ơ空Ҏ(gu)呢?当我们用更有代表性的方法来衡量同步损失Ӟ癑ֈ数很快就下降到可以容忍的范围之内?/p>

?1 把一些这U数据放在一h看。它列D?jin)一些不同的实例Q不同的q_和不同的 JVM 下一个同步的Ҏ(gu)调用相对于一个非同步的方法调用的损失。在每一个实例下Q我q行一个简单的E序Q测定@环调用一个方?10Q?00Q?00 ơ所需的运行时_(d)我调用了(jin)同步和非同步两个版本Qƈ比较?jin)结果。表g的数据是同步版本的运行时间相对于非同步版本的q行旉的比率;它显CZ(jin)同步的性能损失。每ơ运行调用的都是清单 1 中的单方法之一?/p>

表格 1 中显CZ(jin)同步Ҏ(gu)调用相对于非同步Ҏ(gu)调用的相Ҏ(gu)能Qؓ(f)?jin)用l对的标准测定性能损失Q必考虑?JVM 速度提高的因素,qƈ没有在数据中体现出来。在大多数测试中Q每?JVM 的更高版本都?x)?JVM 的M性能得到很大提高Q很有可?1.4 版的 Java 虚拟机发行的时候,它的性能q会(x)有进一步的提高?/p>

?1. 无争用同步的性能损失

JDK staticEmpty empty fetch hashmapGet singleton create
Linux / JDK 1.1 9.2 2.4 2.5 n/a 2.0 1.42
Linux / IBM Java SDK 1.1 33.9 18.4 14.1 n/a 6.9 1.2
Linux / JDK 1.2 2.5 2.2 2.2 1.64 2.2 1.4
Linux / JDK 1.3 (no JIT) 2.52 2.58 2.02 1.44 1.4 1.1
Linux / JDK 1.3 -server 28.9 21.0 39.0 1.87 9.0 2.3
Linux / JDK 1.3 -client 21.2 4.2 4.3 1.7 5.2 2.1
Linux / IBM Java SDK 1.3 8.2 33.4 33.4 1.7 20.7 35.3
Linux / gcj 3.0 2.1 3.6 3.3 1.2 2.4 2.1
Solaris / JDK 1.1 38.6 20.1 12.8 n/a 11.8 2.1
Solaris / JDK 1.2 39.2 8.6 5.0 1.4 3.1 3.1
Solaris / JDK 1.3 (no JIT) 2.0 1.8 1.8 1.0 1.2 1.1
Solaris / JDK 1.3 -client 19.8 1.5 1.1 1.3 2.1 1.7
Solaris / JDK 1.3 -server 1.8 2.3 53.0 1.3 4.2 3.2

清单 1. 基准试中用到的单方?/strong>

public static void staticEmpty() { }
public void empty() { }
public Object fetch() { return field; }
public Object singleton() {
if (singletonField == null)
singletonField
= new Object();
return singletonField;
}
public Object hashmapGet() {
return hashMap.get("this");
}
public Object create() {
return new Object();
}

q些基准测试也阐明?jin)存在动态编译器的情况下解释性能l果所面(f)的挑战。对?1.3 JDK 在有和没?JIT Ӟ数字上的巨大差异需要给Z些解释。对那些非常单的Ҏ(gu)Q?empty ?fetch Q,基准试的本质(它只是执行一个几乎什么也不做的紧凑的循环Q?JIT 可以动态地~译整个循环Q把q行旉压羃到几乎没有的地步。但在一个实际的E序中,JIT 能否q样做就要取决于很多因素?jin),所以,?JIT 的计时数据可能在做公q_比时更有用一些。在M情况下,对于更充实的Ҏ(gu)Q?create ?hashmapGet Q,JIT ׃能象Ҏ(gu)单些的方法那样非同步的情况得到巨大的改q。另外,从数据中看不?JVM 是否能够Ҏ(gu)试的重要部分q行优化。同P在可比较?IBM ?Sun JDK 之间的差异反映了(jin) IBM Java SDK 可以更大E度C化非同步的@环,而不是同步版本代h高。这在纯计时数据中可以明昑֜看出Q这里不提供Q?/p>

从这些数字中我们可以得出以下l论Q对非争用同步而言Q虽然存在性能损失Q但在运行许多不是特别微的Ҏ(gu)Ӟ损失可以降到一个合理的水^Q大多数情况下损失大概在 10% ?200% 之间Q这是一个相对较?yu)的数目Q。所以,虽然同步每个Ҏ(gu)是不明智的(q也?x)增加死锁的可能性)(j)Q但我们也不需要这么害怕同步。这里用的单测试是说明一个无争用同步的代仯比创Z个对象或查找一?code>HashMap 的代价小?/p>

׃早期的书c和文章暗示?jin)无争用同步要付出巨大的性能代h(hun)Q许多程序员q全力避免同步。这U恐惧导致了(jin)许多有问题的技术出玎ͼ比如?double-checked lockingQDCLQ。许多关?Java ~程的书和文章都推荐 DCLQ它看上ȝ是避免不必要的同步的一U聪明的Ҏ(gu)Q但实际上它Ҏ(gu)没有用,应该避免使用它。DCL 无效的原因很复杂Q已出?jin)本文讨论的范围Q要深入?jin)解Q请参阅 参考资?/a>里的链接Q?/p>



回页?/strong>


不要争用

假设同步使用正确Q若U程真正参与争用加锁Q?zhn)也能感受到同步对实际性能的媄(jing)响。ƈ且无争用同步和争用同步间的性能损失差别很大Q一个简单的试E序指出争用同步比无争用同步?50 倍。把q一事实和我们上面抽取的观察数据l合在一P可以看出使用一个争用同步的代h(hun)臛_相当于创?50 个对象?/p>

所以,在调试应用程序中同步的用时Q我们应该努力减实际争用的数目Q而根本不是简单地试图避免使用同步。这个系列的W?2 部分把重点攑֜减少争用的技术上Q包括减锁的粒度、减同步块的大以?qing)减线E间׃n数据的数量?/p>



回页?/strong>


什么时候需要同步?

要(zhn)的E序U程安全Q首先必ȝ定哪些数据将在线E间׃n。如果正在写的数据以后可能被另一个线E读刎ͼ或者正在读的数据可能已l被另一个线E写q了(jin)Q那么这些数据就是共享数据,必须q行同步存取。有些程序员可能?x)惊讶地发现Q这些规则在单地(g)查一个共享引用是否非I的时候也用得上?/p>

许多Z(x)发现q些定义惊hC根{有一U普遍的观点是,如果只是要读一个对象的字段Q不需要请求加锁,其是在 JLS 保证?32 位读操作的原子性的情况下,它更是如此。但不幸的是Q这个观Ҏ(gu)错误的。除非所指的字段被声明ؓ(f) volatile Q否?JMM 不会(x)要求下面的^台提供处理器间的~存一致性和序q诏性,所以很有可能,在某些^CQ没有同步就?x)读到陈旧的数据。有x详细的信息,请参?参考资?/a>?/p>

在确定了(jin)要共享的数据之后Q还要确定要如何保护那些数据。在单情况下Q只需把它们声明ؓ(f) volatile 卛_保护数据字段Q在其它情况下,必须在读或写׃n数据前请求加锁,一个很好的l验是明指Z用什么锁来保护给定的字段或对象,q在你的代码里把它记录下来?/p>

q有一点值得注意的是Q简单地同步存取器方法(或声明下层的字段?volatile Q可能ƈ不以保护一个共享字Dc(din)可以考虑下面的示例:(x)


private int foo;
public synchronized int getFoo() { return foo; }
public synchronized void setFoo(int f) { foo = f; }

如果一个调用者想要增?foo 属性|以下完成该功能的代码׃是线E安全的Q?/p>


setFoo(getFoo()
+ 1);

如果两个U程试图同时增加 foo 属性|l果可能?#160;foo 的值增加了(jin) 1 ?2Q这p时决定。调用者将需要同步一个锁Q才能防止这U争用情况;一个好Ҏ(gu)是在 JavaDoc cM指定同步哪个锁,q样cȝ调用者就不需要自q?jin)?/p>

以上情况是一个很好的CZQ说明我们应该注意多层次_度的数据完整性;同步存取器方法确保调用者能够存取到一致的和最q版本的属性|但如果希望属性的来g当前g_(d)或多个属性间怺一_(d)我们必d步复合操??可能是在一个粗_度的锁上?/p>



回页?/strong>


如果情况不确定,考虑使用同步包装

有时Q在写一个类的时候,我们q不知道它是否要用在一个共享环境里。我们希望我们的cLU程安全的,但我们又不希望给一个L在单U程环境内用的cd上同步的负担Q而且我们可能也不知道使用q个cL合适的锁粒度是多大。幸q的是,通过提供同步包装Q我们可以同时达C上两个目的。Collections cd是这U技术的一个很好的CZQ它们是非同步的Q但在框架中定义的每个接口都有一个同步包装(例如Q?#160;Collections.synchronizedMap() Q,它用一个同步的版本来包装每个方法?/p>



回页?/strong>


l论

虽然 JLS l了(jin)我们可以使我们的E序U程安全的工P但线E安全也不是天上掉下来的馅饼。用同步会(x)蒙受性能损失Q而同步用不当又?x)我们承担数据混ؕ、结果不一致或死锁的风险。幸q的是,在过ȝ几年?JVM 有了(jin)很大的改q,大大减少?jin)与正确使用同步相关的性能损失。通过仔细分析在线E间如何׃n数据Q适当地同步对׃n数据的操作,可以使得(zhn)的E序既是U程安全的,又不?x)承受过多的性能负担?/p>

参考资?/span>



关于作?/span>

Brian Goetz 是一名Y仉问,q且q去 15 q来一直是专业的Y件开发h员。他?#160;QuiotixQ一家坐落在 Los AltosQCalifornia 的Y件开发和咨询公司的首席顾问。请通过 brian@quiotix.com ?Brian 联系?/p>

 



rainman 2008-09-30 13:31 发表评论
]]>
վ֩ģ壺 ޹Ʒlv| ޵һվ| **ɫëƬѹۿ| ߹ۿѸƵ| ŷ޹Ʒ㽶| ҹWWWʪˬ| ŮƵ| ޾Ʒ벻߲HE| hairyëpicsȫ| Ļ߳վ| ޹ƷVA߿| ؼëƬѲ| ĻӰԺѲ| պþþþþ| ŮҰbbwbbw| ѵӰ| Ļav| Ļѹۿ| ۺһʵ| ɫһվ| պaƵѲ| ޹˾þþƷapp| þô| 2022Ļ| ޾ƷŮþþþþ| Ƶ߹ۿ| ޾Ʒ| ³ʦӰԺѹۿ | ƵƵ| ֻˬ͵Ƶ | պƵ| ƵŷƵ| ѻվ߿| ޾Ʒ| | ޾Ʒҹר| 鴤һһgifƵ| 뾫Ʒ˳| һ˿wwwƵ߹ۿ| ѹھƷþþþӰԺ| þҹ޾Ʒ|