﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-rainmanyang-文章分类-java多线程</title><link>http://www.blogjava.net/rainmanyang/category/34918.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 06 Oct 2008 19:01:18 GMT</lastBuildDate><pubDate>Mon, 06 Oct 2008 19:01:18 GMT</pubDate><ttl>60</ttl><item><title>正确使用 Volatile 变量</title><link>http://www.blogjava.net/rainmanyang/articles/232766.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Mon, 06 Oct 2008 13:30:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232766.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232766.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232766.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232766.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: java&#8482; 语言包含两种内在的同步机制：同步块（或方法）和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差（但有时它更简单并且开销更低），而且其使用也更容易出错。在这期的&nbsp;Java 理论与实践&nbsp;中，Brian Goetz 将介绍几种正确使用 volatile 变量的模式，并针对其适用性限制提出一些建议。...&nbsp;&nbsp;<a href='http://www.blogjava.net/rainmanyang/articles/232766.html'>阅读全文</a><img src ="http://www.blogjava.net/rainmanyang/aggbug/232766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-06 21:30 <a href="http://www.blogjava.net/rainmanyang/articles/232766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ThreadLocal与synchronize</title><link>http://www.blogjava.net/rainmanyang/articles/232633.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Mon, 06 Oct 2008 04:13:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232633.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232633.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232633.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232633.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232633.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。synchronized和ThreadLocal都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉，而对ThreadLocal就要陌生得多了。&nbsp;并发问题。当一个对象被两个线程同时访问时，可能有一个线程会得到不可预期的结果。&nbsp;一...&nbsp;&nbsp;<a href='http://www.blogjava.net/rainmanyang/articles/232633.html'>阅读全文</a><img src ="http://www.blogjava.net/rainmanyang/aggbug/232633.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-06 12:13 <a href="http://www.blogjava.net/rainmanyang/articles/232633.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再谈ReentrantLock</title><link>http://www.blogjava.net/rainmanyang/articles/232227.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Fri, 03 Oct 2008 09:55:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232227.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232227.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232227.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232227.html</trackback:ping><description><![CDATA[<span style="font-family: Tahoma; font-size: 12px; line-height: 18px; ">
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><span class="first-letter">重</span>入锁（<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>）是一种递归无阻塞的同步机制。以前一直认为它是synchronized的简单替代，而且实现机制也不相差太远。不过最近实践过程中发现它们之间还是有着天壤之别。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">以下是<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html" target="_blank" style="color: #006699; text-decoration: underline; ">官方说明</a>：一个可重入的互斥锁定 Lock，它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义，但功能更强大。<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span> 将由最近成功获得锁定，并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时，调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定，此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">它提供了lock()方法：<br />
如果该锁定没有被另一个线程保持，则获取该锁定并立即返回，将锁定的保持计数设置为 1。<br />
如果当前线程已经保持该锁定，则将保持计数加 1，并且该方法立即返回。<br />
如果该锁定被另一个线程保持，则出于线程调度的目的，禁用当前线程，并且在获得锁定之前，该线程将一直处于休眠状态，此时锁定保持计数被设置为 1。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">最近在研究<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-tree.html" target="_blank" style="color: #006699; text-decoration: underline; ">Java concurrent</a>中关于任务调度的实现时，读了延迟队列DelayQueue的一些代码，比如take()。该方法的主要功能是从优先队列（PriorityQueue）取出一个最应该执行的任务（最优值），如果该任务的预订执行时间未到，则需要wait这段时间差。反之，如果时间到了，则返回该任务。而offer()方法是将一个任务添加到该队列中。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">后来产生了一个疑问：如果最应该执行的任务是一个小时后执行的，而此时需要提交一个10秒后执行的任务，会出现什么状况？还是先看看take()的源代码：</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> E take() </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ReentrantLock lock </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.lock;<br />
<br />
lock.lockInterruptibly();<br />
<br />
</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "> {<br />
<br />
</span><span style="color: #0000FF; ">for</span><span style="color: #000000; "> (;;) {<br />
<br />
E first </span><span style="color: #000000; ">=</span><span style="color: #000000; "> q.peek();<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (first </span><span style="color: #000000; ">==</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">) {<br />
<br />
available.await();<br />
<br />
} </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "> {<br />
<br />
</span><span style="color: #0000FF; ">long</span><span style="color: #000000; "> delay </span><span style="color: #000000; ">=</span><span style="color: #000000; ">  first.getDelay(TimeUnit.NANOSECONDS);<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (delay </span><span style="color: #000000; ">></span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">) {<br />
<br />
</span><span style="color: #0000FF; ">long</span><span style="color: #000000; "> tl </span><span style="color: #000000; ">=</span><span style="color: #000000; "> available.awaitNanos(delay);<br />
<br />
} </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "> {<br />
<br />
E x </span><span style="color: #000000; ">=</span><span style="color: #000000; "> q.poll();<br />
<br />
</span><span style="color: #0000FF; ">assert</span><span style="color: #000000; "> x </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (q.size() </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />
<br />
available.signalAll(); </span><span style="color: #008000; ">//</span><span style="color: #008000; "> wake up other takers</span><span style="color: #008000; "><br />
</span><span style="color: #000000; "><br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> x;<br />
<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
} </span><span style="color: #0000FF; ">finally</span><span style="color: #000000; "> {<br />
<br />
lock.unlock();<br />
<br />
}<br />
<br />
}</span></div>
<p>而以下是offer()的源代码:</p>
<p>
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public<span style="color: #000000; "> </span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; "> offer(E e) {<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ReentrantLock lock </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.lock;<br />
<br />
lock.lock();<br />
<br />
</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "> {<br />
<br />
E first </span><span style="color: #000000; ">=</span><span style="color: #000000; "> q.peek();<br />
<br />
q.offer(e);<br />
<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (first </span><span style="color: #000000; ">==</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; "> </span><span style="color: #000000; ">||</span><span style="color: #000000; "> e.compareTo(first) </span><span style="color: #000000; ">&lt;</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />
<br />
available.signalAll();<br />
<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />
<br />
} </span><span style="color: #0000FF; ">finally</span><span style="color: #000000; "> {<br />
<br />
lock.unlock();<br />
<br />
}<br />
<br />
}</span></span></div>
</span>
<p><span style="font-family: Tahoma; font-size: 12px; line-height: 18px; ">
</span></p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">如代码所示，take()和offer()都是lock了重入锁。如果按照synchronized的思维（使用诸如synchronized(obj)的方法），这两个方法是互斥的。回到刚才的疑问，take()方法需要等待1个小时才能返回，而offer()需要马上提交一个10秒后运行的任务，会不会一直等待take()返回后才能提交呢？答案是否定的，通过编写验证代码也说明了这一点。这让我对重入锁有了更大的兴趣，它确实是一个无阻塞的锁。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">下面的代码也许能说明问题：运行了4个线程，每一次运行前打印lock的当前状态。运行后都要等待5秒钟。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> main(String[] args) </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ExecutorService exec </span><span style="color: #000000; ">=</span><span style="color: #000000; "> Executors.newFixedThreadPool(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">);<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> ReentrantLock lock </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> ReentrantLock();<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> Condition con </span><span style="color: #000000; ">=</span><span style="color: #000000; "> lock.newCondition();<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> time </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">5</span><span style="color: #000000; ">;<br />
<br />
</span><span style="color: #0000FF; ">final</span><span style="color: #000000; "> Runnable add </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> Runnable() {<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> run() {<br />
<br />
System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Pre </span><span style="color: #000000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">+</span><span style="color: #000000; "> lock);<br />
<br />
lock.lock();<br />
<br />
</span><span style="color: #0000FF; ">try</span><span style="color: #000000; "> {<br />
<br />
con.await(time, TimeUnit.SECONDS);<br />
<br />
} </span><span style="color: #0000FF; ">catch</span><span style="color: #000000; "> (InterruptedException e) {<br />
<br />
e.printStackTrace();<br />
<br />
} </span><span style="color: #0000FF; ">finally</span><span style="color: #000000; "> {<br />
<br />
System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Post </span><span style="color: #000000; ">"</span><span style="color: #000000; "> </span><span style="color: #000000; ">+</span><span style="color: #000000; "> lock.toString());<br />
<br />
lock.unlock();<br />
<br />
}<br />
<br />
}<br />
<br />
};<br />
<br />
</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> index </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">; index </span><span style="color: #000000; ">&lt;</span><span style="color: #000000; "> </span><span style="color: #000000; ">4</span><span style="color: #000000; ">; index</span><span style="color: #000000; ">++</span><span style="color: #000000; ">)<br />
<br />
exec.submit(add);<br />
<br />
exec.shutdown();<br />
<br />
}<br />
</span></div>
<p>&nbsp;</p>
<p><span  style="font-family: Tahoma; font-size: 12px; line-height: 18px; ">
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">这是它的输出：<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-2]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-3]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-4]</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">每一个线程的锁状态都是&#8220;Unlocked&#8221;,所以都可以运行。但在把con.await改成Thread.sleep(5000)时，输出就变成了：<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Unlocked]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Pre&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-1]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-2]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-3]<br />
Post&#160;<span class="hilite1" style="background-color: #ffff00; "><span class="hilite1" style="background-color: #ffff00; ">ReentrantLock</span></span>@a59698[Locked by thread pool-1-thread-4]<br />
</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">以上的对比说明线程在等待时(con.await)，已经不在拥有（keep）该锁了，所以其他线程就可以获得重入锁了。<br />
</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">有必要会过头再看看Java官方的解释：&#8220;如果该锁定被另一个线程保持，则出于线程调度的目的，禁用当前线程，并且在获得锁定之前，该线程将一直处于休眠状态&#8221;。我对这里的&#8220;保持&#8221;的理解是指非wait状态外的所有状态，比如线程Sleep、for循环等一切有CPU参与的活动。一旦线程进入wait状态后，它就不再keep这个锁了，其他线程就可以获得该锁；当该线程被唤醒（触发信号或者timeout）后，就接着执行，会重新&#8220;保持&#8221;锁，当然前提依然是其他线程已经不再&#8220;保持&#8221;了该重入锁。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">总结一句话：对于重入锁而言，"lock"和"keep"是两个不同的概念。lock了锁，不一定keep锁，但keep了锁一定已经lock了锁。</p>
</span></p>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/232227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-03 17:55 <a href="http://www.blogjava.net/rainmanyang/articles/232227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 理论与实践: 流行的原子</title><link>http://www.blogjava.net/rainmanyang/articles/232204.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Fri, 03 Oct 2008 06:35:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/232204.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232204.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232204.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232204.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/232204.html</trackback:ping><description><![CDATA[<span style="font-family: verdana; font-size: 12px; line-height: 19px; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">十五年前，多处理器系统是高度专用系统，要花费数十万美元（大多数具有两个到四个处理器）。现在，多处理器系统很便宜，而且数量很多，几乎每个主要微处理器都内置了多处理支持，其中许多系统支持数十个或数百个处理器。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">要使用多处理器系统的功能，通常需要使用多线程构造应用程序。但是正如任何编写并发应用程序的人可以告诉你的那样，要获得好的硬件利用率，只是简单地在多个线程中分割工作是不够的，还必须确保线程确实大部分时间都在工作，而不是在等待更多的工作，或等待锁定共享数据结构。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">问题：线程之间的协调</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果线程之间 <em>不</em>需要协调，那么几乎没有任务可以真正地并行。以线程池为例，其中执行的任务通常相互独立。如果线程池利用公共工作队列，则从工作队列中删除元素或向工作队列添加元素的过程必须是线程安全的，并且这意味着要协调对头、尾或节点间链接指针所进行的访问。正是这种协调导致了所有问题。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">标准方法：锁定</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">在 Java 语言中，协调对共享字段的访问的传统方法是使用同步，确保完成对共享字段的所有访问，同时具有适当的锁定。通过同步，可以确定（假设类编写正确）具有保护一组给定变量的锁定的所有线程都将拥有对这些变量的独占访问权，并且以后其他线程获得该锁定时，将可以看到对这些变量进行的更改。弊端是如果锁定竞争太厉害（线程常常在其他线程具有锁定时要求获得该锁定），会损害吞吐量，因为竞争的同步非常昂贵。（Public Service Announcement：对于现代 JVM 而言，无竞争的同步现在非常便宜。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">基于锁定的算法的另一个问题是：如果延迟具有锁定的线程（因为页面错误、计划延迟或其他意料之外的延迟），则 <em>没有</em>要求获得该锁定的线程可以继续运行。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">还可以使用可变变量来以比同步更低的成本存储共享变量，但它们有局限性。虽然可以保证其他变量可以立即看到对可变变量的写入，但无法呈现原子操作的读-修改-写顺序，这意味着（比如说）可变变量无法用来可靠地实现互斥（互斥锁定）或计数器。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">使用锁定实现计数器和互斥</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">假如开发线程安全的计数器类，那么这将暴露 <code>get()</code>、 <code>increment()</code> 和 <code>decrement()</code> 操作。清单 1 显示了如何使用锁定（同步）实现该类的例子。注意所有方法，甚至需要同步 <code>get()</code>，使类成为线程安全的类，从而确保没有任何更新信息丢失，所有线程都看到计数器的最新值。</p>
<br />
<a name="listing1"><strong>清单 1. 同步的计数器类</strong></a></span>
<font face="verdana" size="3"><span style="font-size: 12px; line-height: 19px; "><strong>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SynchronizedCounter {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> value;<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value; }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> increment() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #000000; ">++</span><span style="color: #000000; ">value; }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> decrement() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #000000; ">--</span><span style="color: #000000; ">value; }<br />
}</span></div>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><font face=" font-weight: normal; ">
</font></p>
<font face=" font-weight: normal; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><code>increment()</code> 和 <code>decrement()</code> 操作是原子的读-修改-写操作，为了安全实现计数器，必须使用当前值，并为其添加一个值，或写出新值，所有这些均视为一项操作，其他线程不能打断它。否则，如果两个线程试图同时执行增加，操作的不幸交叉将导致计数器只被实现了一次，而不是被实现两次。（注意，通过使值实例变量成为可变变量并不能可靠地完成这项操作。）</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">许多并发算法中都显示了原子的读-修改-写组合。清单 2 中的代码实现了简单的互斥， <code>acquire()</code> 方法也是原子的读-修改-写操作。要获得互斥，必须确保没有其他人具有该互斥（ <code>curOwner = Thread.currentThread()</code>），然后记录您拥有该互斥的事实（ <code>curOwner = Thread.currentThread()</code>），所有这些使其他线程不可能在中间出现以及修改 <code>curOwner field</code>。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing2"><strong>清单 2. 同步的互斥类</strong></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SynchronizedMutex {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> Thread curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> acquire() </span><span style="color: #0000FF; ">throws</span><span style="color: #000000; "> InterruptedException {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (Thread.interrupted()) </span><span style="color: #0000FF; ">throw</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> InterruptedException();<br />
</span><span style="color: #0000FF; ">while</span><span style="color: #000000; "> (curOwner </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">) <br />
wait();<br />
curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> Thread.currentThread();<br />
}<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> release() {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (curOwner </span><span style="color: #000000; ">==</span><span style="color: #000000; "> Thread.currentThread()) {<br />
curOwner </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
notify();<br />
} </span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />
</span><span style="color: #0000FF; ">throw</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> IllegalStateException(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">not owner of mutex</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
}<br />
}</span></div>
</strong></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">清单 1 中的计数器类可以可靠地工作，在竞争很小或没有竞争时都可以很好地执行。然而，在竞争激烈时，这将大大损害性能，因为 JVM 用了更多的时间来调度线程，管理竞争和等待线程队列，而实际工作（如增加计数器）的时间却很少。您可以回想 </span><a href="http://www.ibm.com/developerworks/java/library/j-jtp10264/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" style="color: #5c81a7; "><span style="font-weight: normal;">上月专栏</span></a><span style="font-weight: normal;">中的图，该图显示了一旦多个线程使用同步竞争一个内置监视器，吞吐量将如何大幅度下降。虽然该专栏说明了新的 </span><code><span style="font-weight: normal;">ReentrantLock</span></code><span style="font-weight: normal;"> 类如何可以更可伸缩地替代同步，但是对于一些问题，还有更好的解决方法。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1.2"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-size: 15px; "><span style="font-weight: normal;">锁定问题</span></span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">使用锁定，如果一个线程试图获取其他线程已经具有的锁定，那么该线程将被阻塞，直到该锁定可用。此方法具有一些明显的缺点，其中包括当线程被阻塞来等待锁定时，它无法进行其他任何操作。如果阻塞的线程是高优先级的任务，那么该方案可能造成非常不好的结果（称为 </span><span style="font-weight: normal;">优先级倒置</span><span style="font-weight: normal;">的危险）。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">使用锁定还有一些其他危险，如死锁（当以不一致的顺序获得多个锁定时会发生死锁）。甚至没有这种危险，锁定也仅是相对的粗粒度协调机制，同样非常适合管理简单操作，如增加计数器或更新互斥拥有者。如果有更细粒度的机制来可靠管理对单独变量的并发更新，则会更好一些；在大多数现代处理器都有这种机制。</span></p>
<strong><br />
</strong></font></strong></span><strong><font face=" font-weight: normal; "></font>
<p>&#160;</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><font face=" font-weight: normal; "><strong><br />
</strong>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
</font></p>
<font face=" font-weight: normal; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.0"></a></p>
<span style="font-size: 10pt; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.0"><span class="atitle" style="font-family: Arial, sans-serif; font-size: 18px; "><span style="font-weight: normal;">硬件同步原语</span></span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">如前所述，大多数现代处理器都包含对多处理的支持。当然这种支持包括多处理器可以共享外部设备和主内存，同时它通常还包括对指令系统的增加来支持多处理的特殊要求。特别是，几乎每个现代处理器都有通过可以检测或阻止其他处理器的并发访问的方式来更新共享变量的指令。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-size: 15px; "><span style="font-weight: normal;">比较并交换 (CAS)</span></span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">支持并发的第一个处理器提供原子的测试并设置操作，通常在单位上运行这项操作。现在的处理器（包括 Intel 和 Sparc 处理器）使用的最通用的方法是实现名为 </span><em><span style="font-weight: normal;">比较并转换</span></em><span style="font-weight: normal;">或 CAS 的原语。（在 Intel 处理器中，比较并交换通过指令的 cmpxchg 系列实现。PowerPC 处理器有一对名为&#8220;加载并保留&#8221;和&#8220;条件存储&#8221;的指令，它们实现相同的目地；MIPS 与 PowerPC 处理器相似，除了第一个指令称为&#8220;加载链接&#8221;。）</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">CAS 操作包含三个操作数 —— 内存位置（V）、预期原值（A）和新值(B)。如果内存位置的值与预期原值相匹配，那么处理器会自动将该位置值更新为新值。否则，处理器不做任何操作。无论哪种情况，它都会在 CAS 指令之前返回该位置的值。（在 CAS 的一些特殊情况下将仅返回 CAS 是否成功，而不提取当前值。）CAS 有效地说明了&#8220;我认为位置 V 应该包含值 A；如果包含该值，则将 B 放到这个位置；否则，不要更改该位置，只告诉我这个位置现在的值即可。&#8221;</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">通常将 CAS 用于同步的方式是从地址 V 读取值 A，执行多步计算来获得新值 B，然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改，则 CAS 操作成功。</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><span style="font-weight: normal;">类似于 CAS 的指令允许算法执行读-修改-写操作，而无需害怕其他线程同时修改变量，因为如果其他线程修改变量，那么 CAS 会检测它（并失败），算法可以对该操作重新计算。清单 3 说明了 CAS 操作的行为（而不是性能特征），但是 CAS 的价值是它可以在硬件中实现，并且是极轻量级的（在大多数处理器中）：</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing3"><span style="font-weight: normal;">清单 3. 说明比较并交换的行为（而不是性能）的代码</span></a></p>
</span>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="listing3"><strong></strong></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> SimulatedCAS {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> value;<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value; }<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">synchronized</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> compareAndSwap(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> expectedValue, </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> newValue) {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (value </span><span style="color: #000000; ">==</span><span style="color: #000000; "> expectedValue) <br />
value </span><span style="color: #000000; ">=</span><span style="color: #000000; "> newValue;<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value;<br />
}<br />
}</span></div>
</strong></p>
</font>
<p><span style="font-size: 12px; font-weight: normal; line-height: 19px; ">
</span></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2.2"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">用 CAS 实现计数器</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">基于 CAS 的并发算法称为 <em>无锁定</em>算法，因为线程不必再等待锁定（有时称为互斥或关键部分，这取决于线程平台的术语）。无论 CAS 操作成功还是失败，在任何一种情况中，它都在可预知的时间内完成。如果 CAS 失败，调用者可以重试 CAS 操作或采取其他适合的操作。清单 4 显示了重新编写的计数器类来使用 CAS 替代锁定：</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">class</span><span style="color: #000000; "> CasCounter {<br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> SimulatedCAS value;<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> getValue() {<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> value.getValue();<br />
}<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> increment() {<br />
</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> oldValue </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value.getValue();<br />
</span><span style="color: #0000FF; ">while</span><span style="color: #000000; "> (value.compareAndSwap(oldValue, oldValue </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">) </span><span style="color: #000000; ">!=</span><span style="color: #000000; "> oldValue)<br />
oldValue </span><span style="color: #000000; ">=</span><span style="color: #000000; "> value.getValue();<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> oldValue </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br />
}<br />
}</span></div>
<p>&nbsp;</p>
<p><span  style="font-size: 12px; font-weight: normal; line-height: 19px; ">
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">无锁定且无等待算法</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果每个线程在其他线程任意延迟（或甚至失败）时都将持续进行操作，就可以说该算法是&#160;<em>无等待</em>的。与此形成对比的是，&#160;<em>无锁定</em>算法要求仅&#160;<em>某个</em>线程总是执行操作。（无等待的另一种定义是保证每个线程在其有限的步骤中正确计算自己的操作，而不管其他线程的操作、计时、交叉或速度。这一限制可以是系统中线程数的函数；例如，如果有 10 个线程，每个线程都执行一次&#160;<code>CasCounter.increment()</code>&#160;操作，最坏的情况下，每个线程将必须重试最多九次，才能完成增加。）</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">再过去的 15 年里，人们已经对无等待且无锁定算法（也称为&#160;<em>无阻塞算法</em>）进行了大量研究，许多人通用数据结构已经发现了无阻塞算法。无阻塞算法被广泛用于操作系统和 JVM 级别，进行诸如线程和进程调度等任务。虽然它们的实现比较复杂，但相对于基于锁定的备选算法，它们有许多优点：可以避免优先级倒置和死锁等危险，竞争比较便宜，协调发生在更细的粒度级别，允许更高程度的并行机制等等。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.1"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">原子变量类</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">在 JDK 5.0 之前，如果不使用本机代码，就不能用 Java 语言编写无等待、无锁定的算法。在&#160;<code>java.util.concurrent.atomic</code>&#160;包中添加原子变量类之后，这种情况才发生了改变。所有原子变量类都公开比较并设置原语（与比较并交换类似），这些原语都是使用平台上可用的最快本机结构（比较并交换、加载链接/条件存储，最坏的情况下是旋转锁）来实现的。&#160;<code>java.util.concurrent.atomic</code>&#160;包中提供了原子变量的 9 种风格（&#160;<code>AtomicInteger</code>；&#160;<code>AtomicLong</code>；&#160;<code>AtomicReference</code>；&#160;<code>AtomicBoolean</code>；原子整型；长型；引用；及原子标记引用和戳记引用类的数组形式，其原子地更新一对值）。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">原子变量类可以认为是&#160;<code>volatile</code>&#160;变量的泛化，它扩展了可变变量的概念，来支持原子条件的比较并设置更新。读取和写入原子变量与读取和写入对可变变量的访问具有相同的存取语义。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">虽然原子变量类表面看起来与清单 1 中的&#160;<code>SynchronizedCounter</code>&#160;例子一样，但相似仅是表面的。在表面之下，原子变量的操作会变为平台提供的用于并发访问的硬件原语，比如比较并交换。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.2"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">更细粒度意味着更轻量级</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">调整具有竞争的并发应用程序的可伸缩性的通用技术是降低使用的锁定对象的粒度，希望更多的锁定请求从竞争变为不竞争。从锁定转换为原子变量可以获得相同的结果，通过切换为更细粒度的协调机制，竞争的操作就更少，从而提高了吞吐量。</p>
<table align="right" border="0" cellspacing="0" cellpadding="0" width="40%">
    <tbody>
        <tr>
            <td width="10" style="line-height: 19px; "><img alt="" height="1" width="10" src="http://www.ibm.com/i/c.gif" /></td>
            <td style="line-height: 19px; ">
            <table border="1" cellspacing="0" cellpadding="5" width="100%">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee" style="line-height: 19px; "><a name="sidebar1"><strong>ABA 问题</strong></a><br />
                        因为在更改 V 之前，CAS 主要询问&#8220;V 的值是否仍为 A&#8221;，所以在第一次读取 V 以及对 V 执行 CAS 操作之前，如果将值从 A 改为 B，然后再改回 A，会使基于 CAS 的算法混乱。在这种情况下，CAS 操作会成功，但是在一些情况下，结果可能不是您所预期的。（注意，&#160;<a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#listing1" style="color: #5c81a7; ">清单 1</a>&#160;和&#160;<a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#listing2" style="color: #5c81a7; ">清单 2</a>&#160;中的计数器和互斥例子不存在这个问题，但不是所有算法都这样。）这类问题称为&#160;<em>ABA 问题</em>，通常通过将标记或版本编号与要进行 CAS 操作的每个值相关联，并原子地更新值和标记，来处理这类问题。&#160;<code>AtomicStampedReference</code>&#160;类支持这种方法。</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3.3"><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">java.util.concurrent 中的原子变量</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">无论是直接的还是间接的，几乎&#160;<code>java.util.concurrent</code>&#160;包中的所有类都使用原子变量，而不使用同步。类似<code>ConcurrentLinkedQueue</code>&#160;的类也使用原子变量直接实现无等待算法，而类似&#160;<code>ConcurrentHashMap</code>&#160;的类使用&#160;<code>ReentrantLock</code>&#160;在需要时进行锁定。然后，&#160;<code>ReentrantLock</code>&#160;使用原子变量来维护等待锁定的线程队列。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果没有 JDK 5.0 中的 JVM 改进，将无法构造这些类，这些改进暴露了（向类库，而不是用户类）接口来访问硬件级的同步原语。然后，java.util.concurrent 中的原子变量类和其他类向用户类公开这些功能。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="4.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">使用原子变量获得更高的吞吐量</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a href="http://www.ibm.com/developerworks/java/library/j-jtp10264/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" style="color: #5c81a7; ">上月</a>，我介绍了&#160;<code>ReentrantLock</code>&#160;如何相对于同步提供可伸缩性优势，以及构造通过伪随机数生成器模拟旋转骰子的简单、高竞争示例基准。我向您显示了通过同步、&#160;<code>ReentrantLock</code>&#160;和公平&#160;<code>ReentrantLock</code>&#160;来进行协调的实现，并显示了结果。本月，我将向该基准添加其他实现，使用&#160;<code>AtomicLong</code>&#160;更新 PRNG 状态的实现。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">清单 5 显示了使用同步的 PRNG 实现和使用 CAS 备选实现。注意，要在循环中执行 CAS，因为它可能会失败一次或多次才能获得成功，使用 CAS 的代码总是这样。</p>
<br />
<a name="listing5"><strong>清单 5. 使用同步和原子变量实现线程安全 PRNG</strong></a><br />
<table width="100%" cellpadding="0" cellspacing="0" border="0">
    <tbody>
        <tr>
            <td class="code-outline" style="line-height: 19px; background-color: #eeeeee; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; ">
            <pre class="displaycode" style="margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, fixed, monospace; font-size: 11px; ">
            public class PseudoRandomUsingSynch implements PseudoRandom {
            private int seed;
            public PseudoRandomUsingSynch(int s) { seed = s; }
            public synchronized int nextInt(int n) {
            int s = seed;
            seed = Util.calculateNext(seed);
            return s % n;
            }
            }
            public class PseudoRandomUsingAtomic implements PseudoRandom {
            private final AtomicInteger seed;
            public PseudoRandomUsingAtomic(int s) {
            seed = new AtomicInteger(s);
            }
            public int nextInt(int n) {
            for (;;) {
            int s = seed.get();
            int nexts = Util.calculateNext(s);
            if (seed.compareAndSet(s, nexts))
            return s % n;
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">下面图 1 和图 2 中的图与上月那些图相似，只是为基于原子的方法多添加了一行。这些图显示了在 8-way Ultrasparc3 和单处理器 Pentium 4 上使用不同数量线程的随机发生的吞吐量（以每秒转数为单位）。测试中的线程数不是真实的；这些线程所表现的竞争比通常多得多，所以它们以比实际程序中低得多的线程数显示了&#160;<code>ReentrantLock</code>&#160;与原子变量之间的平衡。您将看到，虽然&#160;<code>ReentrantLock</code>&#160;拥有比同步更多的优点，但相对于&#160;<code>ReentrantLock</code>，原子变量提供了其他改进。（因为在每个工作单元中完成的工作很少，所以下图可能无法完全地说明与 ReentrantLock 相比，原子变量具有哪些可伸缩性优点。）</p>
<br />
<a name="fig1"><strong>图 1. 8-way Ultrasparc3 中同步、ReentrantLock、公平 Lock 和 AtomicLong 的基准吞吐量</strong></a><br />
<img alt="8-way Ultrasparc3 吞吐量" height="332" src="http://www.ibm.com/developerworks/cn/java/j-jtp11234/RngThroughput.gif" width="550" />&#160;<br />
<br />
<a name="fig2"><strong>图 2. 单处理器 Pentium 4 中的同步、ReentrantLock、公平 Lock 和 AtomicLong 的基准吞吐量</strong></a><br />
<img alt="Uniprocessor Pentium4 吞吐量" height="328" src="http://www.ibm.com/developerworks/cn/java/j-jtp11234/RngThroughputUni.gif" width="551" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">大多数用户都不太可能使用原子变量自己开发无阻塞算法 — 他们更可能使用&#160;<code>java.util.concurrent</code>&#160;中提供的版本，如&#160;<code>ConcurrentLinkedQueue</code>。但是万一您想知道对比以前 JDK 中的相类似的功能，这些类的性能是如何改进的，可以使用通过原子变量类公开的细粒度、硬件级别的并发原语。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">开发人员可以直接将原子变量用作共享计数器、序号生成器和其他独立共享变量的高性能替代，否则必须通过同步保护这些变量。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="5.0"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">结束语</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">JDK 5.0 是开发高性能并发类的巨大进步。通过内部公开新的低级协调原语，和提供一组公共原子变量类，现在用 Java 语言开发无等待、无锁定算法首次变为可行。然后，&#160;<code>java.util.concurrent</code>&#160;中的类基于这些低级原子变量工具构建，为它们提供比以前执行相似功能的类更显著的可伸缩性优点。虽然您可能永远不会直接使用原子变量，还是应该为它们的存在而欢呼。</p>
</span></p>
</strong></font>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/232204.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-03 14:35 <a href="http://www.blogjava.net/rainmanyang/articles/232204.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现 Java 多线程并发控制框架</title><link>http://www.blogjava.net/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.blogjava.net/rainmanyang/articles/232135.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/232135.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/232135.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/232135.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/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 class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">所面临的问题</span></a></p>
<br />
<a name="N10042"><strong>图 1. 线程场景</strong></a><br />
<img alt="图 1. 线程场景" 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; ">这幅图中节点代表一个 single Thread，边代表执行的步骤。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">整幅图代表的意思是，ROOT 线程执行完毕后执行 T1 线程，T1 执行完毕后并发的执行 T2 和 T3。而从 T2 和 T3 指向 T4 的两条边表示的是 T4 必须等 T2 和 T3 都执行完毕以后才能开始执行。剩下的步骤以此类推，直到 END 作为整个过程的结束。当然，这只是个简略的示意图，可能面对的一个线程场景会有上百个线程。还有，你可以观察到这整个场景只有一个入口点和一个出口点，这意味着什么？在下文中为你解释。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">这其中涉及到了 Java 线程的同步互斥机制。例如如何让 T1 在 T2 和 T3 之前运行，如何让 T2 和 T3 都执行完毕之后开启 T4 线程。</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 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; ">如何来描述图 1 中所示的场景呢？可以采用 XML 的格式来描述我们的模型。我定义一个&#8220;Thread&#8221; element 来表示线程。</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; ">&lt;</span><span style="color: #000000; ">ThreadList</span><span style="color: #000000; ">></span><span style="color: #000000; "><br />
</span><span style="color: #000000; ">&lt;</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&#8230;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">>&lt;/</span><span style="color: #000000; ">Thread</span><span style="color: #000000; ">></span><span style="color: #000000; "><br />
</span><span style="color: #000000; ">&lt;</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&#8230;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">>&lt;/</span><span style="color: #000000; ">Thread</span><span style="color: #000000; ">></span><span style="color: #000000; "><br />
</span><span style="color: #000000; ">&lt;/</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 是线程的唯一标识符，PRETHREAD 便是该线程的直接先决线程的ID，每个线程 ID 之间用逗号隔开。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">在 Thread 这个 element 里面可以加入你想要该线程执行任务的具体信息。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">实际上模型的描述是解决问题非常重要的一个环节，整个线程场景可以用一种一致的形式来描述，作为 Java 多线程并发控制框架引擎的输入。也就是将线程运行的模式用 XML 来描述出来，这样只用改动 XML 配置文件就可以更改整个线程运行的模式，不用改动任何的源代码。</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 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 多线程的运行框架来说，我们将采用&#8220;外&#8221;和&#8220;内&#8221;的两种模式来实现。</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 class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">&#8220;外&#8221; - 主线程轮询</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" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Thread 是工作线程。ThreadEntry 是 Thread 的包装类，prerequisite 是一个 HashMap，它含有 Thread 的先决线程的状态。如图1中显示的那样，T4 的先决线程是 T2 和 T3，那么 prerequisite 中就包含 T2 和 T3 的状态。TestScenario 中的 threadEntryList 中包含所有的 ThreadEntry。</p>
<br />
<a name="N10093"><strong>图 3. 线程执行场景</strong></a><br />
<img alt="图 3. 线程执行场景" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image003.gif" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">TestScenario 作为主线程，作为一个&#8220;外&#8221;在的监控者，不断地轮询 threadEntryList 中所有 ThreadEntry 的状态，当 ThreadEntry 接受到 isReady 的查询后查询自己的 prerequisite，当其中所有的先决线程的状态为&#8220;正常结束时&#8221;，它便返回 ready，那么 TestScenario 便会调用 ThreadEntry 的 startThread() 方法授权该 ThreadEntry 运行线程，Thread 便通过 run() 方法来真正执行线程。并在正常执行完毕后调用 setPreRequisteState() 方法来更新整个 Scenario，threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有该 Thread 的状态信息为&#8220;正常结束&#8221;。</p>
<br />
<a name="N100A6"><strong>图 4. 状态更改的过程</strong></a><br />
<img alt="图 4. 状态更改的过程" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image004.jpg" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如图 1 中所示的 T4 的先决线程为 T2 和 T3，T2 和 T3 并行执行。如图 4 所示，假设 T2 先执行完毕，它会调用 setPreRequisteState() 方法来更新整个 Scenario， threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有该 T2 的状态信息为&#8220;正常结束&#8221;。此时，T4 的 prerequisite 中 T2 的状态为&#8220;正常结束&#8221;，但是 T3 还没有执行完毕，所以其状态为&#8220;未完毕&#8221;。所以 T4 的 isReady 查询返回为 false，T4 不会执行。只有当 T3 执行完毕后更新状态为&#8220;正常结束&#8221;后，T4 的状态才为 ready，T4 才会开始运行。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">其余的节点也以此类推，它们正常执行完毕的时候会在整个的 scenario 中广播该线程正常结束的信息，由主线程不断地轮询各个 ThreadEntry 的状态来开启各个线程。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">这便是采用主控线程轮询状态表的方式来控制 Java 多线程运行框架的实现方式之一。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>优点：</strong>概念结构清晰明了，实现简单。避免采用 Java 的锁机制，减少产生死锁的几率。当发生异常导致其中某些线程不能正常执行完毕的时候，不会产生挂起的线程。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>缺点：</strong>采用主线程轮询机制，耗费 CPU 时间。当图中的节点太多的(n>??? 而线程单个线程执行时间比较短的时候 t&lt;??? 需要进一步研究)时候会产生线程启动的些微延迟，也就是说实时性能在极端情况下不好，当然这可以另外写一篇文章来专门探讨。</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 class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">&#8220;内&#8221; - wait&amp;notify</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">相对于&#8220;外&#8221;-主线程轮询机制来说，&#8220;内&#8221;采用的是自我控制连锁触发机制。</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" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Thread 中的 lock 为当前 Thread 的 lock，lockList 是一个 HashMap，持有其后继线程的 lock 的引用，getLock 和 setLock 可以对 lockList 中的 Lock 进行操作。其中很重要的一个成员是 waitForCount，这是一个引用计数。表明当前线程正在等待的先决线程的个数，例如图 1 中所示的 T4，在初始的情况下，他等待的先决线程是 T2 和 T3，那么它的 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" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">当整个过程开始运行的时候，我们将所有的线程 start，但是每个线程所持的 lock 都处于 wait 状态，线程都会处于 waiting 的状态。此时，我们将 root thread 所持有的自身的 lock notify，这样 root thread 就会运行起来。当 root 的 run 方法执行完毕以后。它会检查其后续线程的 waitForCount，并将其值减一。然后再次检查 waitForCount，如果 waitForCount 等于 0，表示该后续线程的所有先决线程都已经执行完毕，此时我们 notify 该线程的 lock，该后续线程便可以从 waiting 的状态转换成为 running 的状态。然后这个过程连锁递归的进行下去，整个过程便会执行完毕。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">我们还是以 T2，T3，T4 为例，当进行 initThreadLock 过程的时候，我们可以知道 T4 有两个直接先决线程 T2 和 T3，所以 T4 的 waitForCount 等于 2。我们假设 T3 先执行完毕，T2 仍然在 running 的状态，此时他会首先遍历其所有的直接后继线程，并将他们的 waitForCount 减去 1，此时他只有一个直接后继线程 T4，于是 T4 的 waitForCount 减去 1 以后值变为 1，不等于 0，此时不会将 T4 的 lock notify，T4 继续 waiting。当 T2 执行完毕之后，他会执行与 T3 相同的步骤，此时 T4 的 waitForCount 等于 0，T2 便 notify T4 的 lock，于是 T4 从 waiting 状态转换成为 running 状态。其他的节点也是相似的情况。</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; "><strong>优点：</strong>采用 wait&amp;notify 机制而不采用轮询的机制，不会浪费CPU资源。执行效率较高。而且相对于&#8220;外&#8221;-主线程轮询的机制来说实时性更好。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><strong>缺点：</strong>采用 Java 线程 Object 的锁机制，实现起来较为复杂。而且采取一种连锁触发的方式，如果其中某些线程异常，会导致所有其后继线程的挂起而造成整个 scenario 的运行失败。为了防止这种情况的发生，我们还必须建立一套线程监控的机制来确保其正常运行。</p>
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N10106"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">延伸</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">下面的图所要表达的是这样一种递归迭代的概念。例如在图1 中展示的那样，T1 这个节点表示的是一个线程。现在，忘掉线程这样一个概念，将 T1 抽象为一个过程，想象它是一个银河系，深入到 T1 中去，它也是一个许多子过程的集合，这些子过程之间的关系模式就如图 1 所示那样，可以用一个图来表示。</p>
<br />
<a name="N10111"><strong>图 7. 嵌套子过程</strong></a><br />
<img alt="图 7. 嵌套子过程" border="0" src="http://www.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image007.jpg" />&#160;<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">可以想象一下这是怎样的一个框架，具有无穷扩展性的过程框架，我们只用定义各个过程之间的关系，我们不用关心过程是怎样运行的。事实上，可以在最终的节点上指定一个实际的工作，比如读一个文件，或者submit一个JCL job，或者执行一条sql statement。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">其实，按照某种遍历规则，完全可以将这种嵌套递归的结构转化成为一个一层扁平结构的图，而不是原来的分层的网状结构，但是我们不这样做的原因是基于以下的几点考虑：</p>
<ol style="margin-top: 2px; margin-bottom: 2px; padding-top: 2px; padding-bottom: 2px; ">
    <li>如果这样做，会导致图节点太多，边太多，令人眼花缭乱。</li>
    <li>不这样做更主要的原因是每一个场景，如图 7 中的 T1，T13，是状态聚集的一个单元，具有高复用性和可靠性。</li>
    <li>框架是高度抽象的，它实际的执行可以是分布式的，一个单元可以是一个系统，作为和其他系统的分界标志。</li>
</ol>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">实际上，这是一个状态聚集的层次控制框架，我们可以依赖此框架来执行自主运算。我们将在其它的文章中来讨论它的应用。</p>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="N10134"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">总结</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">本文介绍了一种 Java 多线程并发控制的框架，并给出了其两种实现的模型，它们有各自的优缺点，有各自的适用范围。当需要进行 Java 线程的并发控制的时候，可以作为参考。</p>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="resources"><span 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 的文章：<a href="http://www.ibm.com/developerworks/java/library/j-spnotif.html?S_TACT=105AGX52&amp;S_CMP=cn-a-j" target="_blank" style="color: #5c81a7; ">Apply the Specific Notification pattern to control the order of thread execution</a><br />
    <br />
    </li>
    <li>Doug Lea 的著名并发性图书：<a href="http://www.china-pub.com/computers/common/info.asp?id=16303" target="_blank" style="color: #5c81a7; "><em>Java 并发编程: 设计原则与模式. 第二版(Addison Wesley 1999)</em></a><br />
    <br />
    </li>
    <li>另一本关于并发性的图书：<a href="http://www.ibm.com/developerworks/cn/java/j-goetzbook.html" style="color: #5c81a7; "><em>Java Concurrency in Practice</em></a><br />
    <br />
    </li>
    <li>developerWorks Java 专区 Joseph Hartal，Ze'ev Bubis 的文章：<a href="http://www.ibm.com/developerworks/cn/java/j-prodcon/" style="color: #996699; ">使你轻松得进行多线程应用程序编程</a><br />
    <br />
    </li>
    <li>developerWorks Java 专区 Alex Roetter 的文章：<a href="http://www.ibm.com/developerworks/cn/java/j-thread/" style="color: #5c81a7; ">编写多线程的Java应用程序</a><br />
    <br />
    </li>
    <li>developerWorks Java 专区 Neel V. Kumar 的文章：<a href="http://www.ibm.com/developerworks/cn/java/multithreading/" style="color: #996699; ">Java 程序中的多线程</a></li>
</ul>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="author"><span 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; ">陈威，华中科技大学硕士，IBM CSDL Software Engineer，所在的 Team 是 DB2 for z/OS。联系方式：chenwbj@cn.ibm.com</p>
            <div><br />
            </div>
            </td>
        </tr>
    </tbody>
</table>
</p>
</p>
</p>
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</span>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/232135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-10-02 23:12 <a href="http://www.blogjava.net/rainmanyang/articles/232135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Synchronization and the Java Memory Model</title><link>http://www.blogjava.net/rainmanyang/articles/231899.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Tue, 30 Sep 2008 08:55:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/231899.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/231899.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/231899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/231899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/231899.html</trackback:ping><description><![CDATA[<blockquote><em>This set of excerpts from section 2.2 includes the main discussions on how the Java Memory Model impacts concurrent programming.</em>
<p><em>For information about ongoing work on the memory model, see&#160;<a href="http://www.cs.umd.edu/~pugh/java/memoryModel/">Bill Pugh's Java Memory Model pages</a>.</em></p>
</blockquote>
<p>&nbsp;</p>
<p>Consider the tiny class, defined without any synchronization:</p>
<pre>final class SetCheck {
private int  a = 0;
private long b = 0;
void set() {
a =  1;
b = -1;
}
boolean check() {
return ((b ==  0) ||
(b == -1 &amp;&amp; a == 1));
}
}
</pre>
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:
<ul>
    <li>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.</li>
    <li>The processor may rearrange the execution order of machine instructions corresponding to the statements, or even execute them at the same time.
    <p>&nbsp;</p>
    </li>
    <li>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.
    <p>&nbsp;</p>
    </li>
    <li>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.
    <p>&nbsp;</p>
    </li>
    <li>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.</li>
</ul>
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.
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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 �4).</p>
<p>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.</p>
<p>The assumptions underlying the model can be viewed as an idealization of a standard SMP machine of the sort described in �2.4:</p>
<p><img src="http://gee.cs.oswego.edu/dl/cpj/mm-1.gif"  alt="" /></p>
<p>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.</p>
<p>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:</p>
<dl><dt>Atomicity</dt><dd>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.</dd><dt>Visibility</dt><dd>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.</dd><dt>Ordering</dt><dd>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.</dd></dl>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.
<p>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.</p>
<p>The most important rules and properties specified by the model are discussed below.</p>
<p>&nbsp;</p>
<h3>Atomicity</h3>
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.)
<p>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.</p>
<p>&nbsp;</p>
<h3>Visibility</h3>
Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions:
<ul>
    <li>A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.
    <p>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.</p>
    <p>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.</p>
    <p>&nbsp;</p>
    </li>
    <li>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.
    <p>&nbsp;</p>
    </li>
    <li>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.
    <p>Among other consequences, it is bad practice to make available the reference to an incompletely constructed object (see �1.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.</p>
    <p>&nbsp;</p>
    </li>
    <li>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 �3.2).</li>
</ul>
Note that visibility problems never arise when passing references to objects across methods in the same thread.
<p>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 �2.6).</p>
<p>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.</p>
<p>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.</p>
<p>&nbsp;</p>
<h3>Ordering</h3>
Ordering rules fall under two cases, within-thread and between-thread:
<ul>
    <li>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.
    <p>&nbsp;</p>
    </li>
    <li>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.</li>
</ul>
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.
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>&nbsp;</p>
<h2>Volatile</h2>
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:
<pre>final class VFloat {
private float value;
final synchronized void  set(float f) { value = f; }
final synchronized float get()        { return value; }
}
</pre>
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.
<p>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.</p>
<p>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.</p>
<p>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:</p>
<p>&nbsp;</p>
<ul>
    <li>The field need not obey any invariants with respect to others.
    <p>&nbsp;</p>
    </li>
    <li>Writes to the field do not depend on its current value.
    <p>&nbsp;</p>
    </li>
    <li>No thread ever writes an illegal value with respect to intended semantics.</li>
    <li>The actions of readers do not depend on values of other non-volatile fields.</li>
</ul>
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 �4.2, a volatile can be useful as a completion flag. Additional examples are illustrated in �4, 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.
<img src ="http://www.blogjava.net/rainmanyang/aggbug/231899.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-09-30 16:55 <a href="http://www.blogjava.net/rainmanyang/articles/231899.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>轻松使用线程: 同步不是敌人(转载自ibm developwork)</title><link>http://www.blogjava.net/rainmanyang/articles/231868.html</link><dc:creator>rainman</dc:creator><author>rainman</author><pubDate>Tue, 30 Sep 2008 05:31:00 GMT</pubDate><guid>http://www.blogjava.net/rainmanyang/articles/231868.html</guid><wfw:comment>http://www.blogjava.net/rainmanyang/comments/231868.html</wfw:comment><comments>http://www.blogjava.net/rainmanyang/articles/231868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rainmanyang/comments/commentRss/231868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rainmanyang/services/trackbacks/231868.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; ">大多数编程语言的语言规范都不会谈到线程和并发的问题；因为一直以来，这些问题都是留给平台或操作系统去详细说明的。但是，Java 语言规范（JLS）却明确包括一个线程模型，并提供了一些语言元素供开发人员使用以保证他们程序的线程安全。</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; ">许多用户第一次发现他们不得不去理解线程的概念的时候，并不是因为他们在写创建和管理线程的程序，而是因为他们正在用一个本身是多线程的工具或框架。任何用过 Swing GUI 框架或写过小服务程序或 JSP 页的开发人员（不管有没有意识到）都曾经被线程的复杂性困扰过。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">Java 设计师是想创建一种语言，使之能够很好地运行在现代的硬件，包括多处理器系统上。要达到这一目的，管理线程间协调的工作主要推给了软件开发人员；程序员必须指定线程间共享数据的位置。在 Java 程序中，用来管理线程间协调工作的主要工具是 <code>synchronized</code> 关键字。在缺少同步的情况下，JVM 可以很自由地对不同线程内执行的操作进行计时和排序。在大部分情况下，这正是我们想要的，因为这样可以提高性能，但它也给程序员带来了额外的负担，他们不得不自己识别什么时候这种性能的提高会危及程序的正确性。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="1"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; "><code>synchronized</code> <em>真正</em>意味着什么？</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">大部分 Java 程序员对同步的块或方法的理解是完全根据使用互斥（互斥信号量）或定义一个临界段（一个必须原子性地执行的代码块）。虽然 <code>synchronized</code> 的语义中确实包括互斥和原子性，但在管程进入之前和在管程退出之后发生的事情要复杂得多。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><code>synchronized</code> 的语义确实保证了一次只有一个线程可以访问被保护的区段，但同时还包括同步线程在主存内互相作用的规则。理解 Java 内存模型（JMM）的一个好方法就是把各个线程想像成运行在相互分离的处理器上，所有的处理器存取同一块主存空间，每个处理器有自己的缓存，但这些缓存可能并不总和主存同步。在缺少同步的情况下，JMM 会允许两个线程在同一个内存地址上看到不同的值。而当用一个管程（锁）进行同步的时候，一旦申请加了锁，JMM 就会马上要求该缓存失效，然后在它被释放前对它进行刷新（把修改过的内存位置写回主存）。不难看出为什么同步会对程序的性能影响这么大；频繁地刷新缓存代价会很大。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><br />
</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-threads/#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="2"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">使用一条好的运行路线</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果同步不适当，后果是很严重的：会造成数据混乱和争用情况，导致程序崩溃，产生不正确的结果，或者是不可预计的运行。更糟的是，这些情况可能很少发生且具有偶然性（使得问题很难被监测和重现）。如果测试环境和开发环境有很大的不同，无论是配置的不同，还是负荷的不同，都有可能使得这些问题在测试环境中根本不出现，从而得出错误的结论：我们的程序是正确的，而事实上这些问题只是还没出现而已。</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="N10086"><strong>争用情况定义</strong></a><br />
                        <p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><em>争用情况</em>是一种特定的情况：两个或更多的线程或进程读或写一些共享数据，而最终结果取决于这些线程是如何被调度计时的。争用情况可能会导致不可预见的结果和隐蔽的程序错误。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">另一方面，不当或过度地使用同步会导致其它问题，比如性能很差和死锁。当然，性能差虽然不如数据混乱那么严重，但也是一个严重的问题，因此同样不可忽视。编写优秀的多线程程序需要使用好的运行路线，足够的同步可以使您的数据不发生混乱，但不需要滥用到去承担死锁或不必要地削弱程序性能的风险。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-threads/#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="3"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">同步的代价有多大？</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">由于包括缓存刷新和设置失效的过程，Java 语言中的同步块通常比许多平台提供的临界段设备代价更大，这些临界段通常是用一个原子性的&#8220;test and set bit&#8221;机器指令实现的。即使一个程序只包括一个在单一处理器上运行的单线程，一个同步的方法调用仍要比非同步的方法调用慢。如果同步时还发生锁定争用，那么性能上付出的代价会大得多，因为会需要几个线程切换和系统调用。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">幸运的是，随着每一版的 JVM 的不断改进，既提高了 Java 程序的总体性能，同时也相对减少了同步的代价，并且将来还可能会有进一步的改进。此外，同步的性能代价经常是被夸大的。一个著名的资料来源就曾经引证说一个同步的方法调用比一个非同步的方法调用慢 50 倍。虽然这句话有可能是真的，但也会产生误导，而且已经导致了许多开发人员即使在需要的时候也避免使用同步。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">严格依照百分比计算同步的性能损失并没有多大意义，因为一个无争用的同步给一个块或方法带来的是固定的性能损失。而这一固定的延迟带来的性能损失百分比取决于在该同步块内做了多少工作。对一个<em>空</em>方法的同步调用可能要比对一个空方法的非同步调用慢 20 倍，但我们多长时间才调用一次空方法呢？当我们用更有代表性的小方法来衡量同步损失时，百分数很快就下降到可以容忍的范围之内。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">表 1 把一些这种数据放在一起来看。它列举了一些不同的实例，不同的平台和不同的 JVM 下一个同步的方法调用相对于一个非同步的方法调用的损失。在每一个实例下，我运行一个简单的程序，测定循环调用一个方法 10，000，000 次所需的运行时间，我调用了同步和非同步两个版本，并比较了结果。表格中的数据是同步版本的运行时间相对于非同步版本的运行时间的比率；它显示了同步的性能损失。每次运行调用的都是清单 1 中的简单方法之一。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">表格 1 中显示了同步方法调用相对于非同步方法调用的相对性能；为了用绝对的标准测定性能损失，必须考虑到 JVM 速度提高的因素，这并没有在数据中体现出来。在大多数测试中，每个 JVM 的更高版本都会使 JVM 的总体性能得到很大提高，很有可能 1.4 版的 Java 虚拟机发行的时候，它的性能还会有进一步的提高。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name=" "><span class="smalltitle" style="font-family: arial, nsimsun, sans-serif; font-weight: bold; font-size: 15px; ">表 1. 无争用同步的性能损失</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "></p>
<table border="1" cellpadding="1" cellspacing="1">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><strong>JDK</strong></td>
            <td style="line-height: 19px; "><strong>staticEmpty</strong></td>
            <td style="line-height: 19px; "><strong>empty</strong></td>
            <td style="line-height: 19px; "><strong>fetch</strong></td>
            <td style="line-height: 19px; "><strong>hashmapGet</strong></td>
            <td style="line-height: 19px; "><strong>singleton</strong></td>
            <td style="line-height: 19px; "><strong>create</strong></td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / JDK 1.1</td>
            <td style="line-height: 19px; ">9.2</td>
            <td style="line-height: 19px; ">2.4</td>
            <td style="line-height: 19px; ">2.5</td>
            <td style="line-height: 19px; ">n/a</td>
            <td style="line-height: 19px; ">2.0</td>
            <td style="line-height: 19px; ">1.42</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / IBM Java SDK 1.1</td>
            <td style="line-height: 19px; ">33.9</td>
            <td style="line-height: 19px; ">18.4</td>
            <td style="line-height: 19px; ">14.1</td>
            <td style="line-height: 19px; ">n/a</td>
            <td style="line-height: 19px; ">6.9</td>
            <td style="line-height: 19px; ">1.2</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / JDK 1.2</td>
            <td style="line-height: 19px; ">2.5</td>
            <td style="line-height: 19px; ">2.2</td>
            <td style="line-height: 19px; ">2.2</td>
            <td style="line-height: 19px; ">1.64</td>
            <td style="line-height: 19px; ">2.2</td>
            <td style="line-height: 19px; ">1.4</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / JDK 1.3 (no JIT)</td>
            <td style="line-height: 19px; ">2.52</td>
            <td style="line-height: 19px; ">2.58</td>
            <td style="line-height: 19px; ">2.02</td>
            <td style="line-height: 19px; ">1.44</td>
            <td style="line-height: 19px; ">1.4</td>
            <td style="line-height: 19px; ">1.1</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / JDK 1.3 -server</td>
            <td style="line-height: 19px; ">28.9</td>
            <td style="line-height: 19px; ">21.0</td>
            <td style="line-height: 19px; ">39.0</td>
            <td style="line-height: 19px; ">1.87</td>
            <td style="line-height: 19px; ">9.0</td>
            <td style="line-height: 19px; ">2.3</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / JDK 1.3 -client</td>
            <td style="line-height: 19px; ">21.2</td>
            <td style="line-height: 19px; ">4.2</td>
            <td style="line-height: 19px; ">4.3</td>
            <td style="line-height: 19px; ">1.7</td>
            <td style="line-height: 19px; ">5.2</td>
            <td style="line-height: 19px; ">2.1</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / IBM Java SDK 1.3</td>
            <td style="line-height: 19px; ">8.2</td>
            <td style="line-height: 19px; ">33.4</td>
            <td style="line-height: 19px; ">33.4</td>
            <td style="line-height: 19px; ">1.7</td>
            <td style="line-height: 19px; ">20.7</td>
            <td style="line-height: 19px; ">35.3</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Linux / gcj 3.0</td>
            <td style="line-height: 19px; ">2.1</td>
            <td style="line-height: 19px; ">3.6</td>
            <td style="line-height: 19px; ">3.3</td>
            <td style="line-height: 19px; ">1.2</td>
            <td style="line-height: 19px; ">2.4</td>
            <td style="line-height: 19px; ">2.1</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Solaris / JDK 1.1</td>
            <td style="line-height: 19px; ">38.6</td>
            <td style="line-height: 19px; ">20.1</td>
            <td style="line-height: 19px; ">12.8</td>
            <td style="line-height: 19px; ">n/a</td>
            <td style="line-height: 19px; ">11.8</td>
            <td style="line-height: 19px; ">2.1</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Solaris / JDK 1.2</td>
            <td style="line-height: 19px; ">39.2</td>
            <td style="line-height: 19px; ">8.6</td>
            <td style="line-height: 19px; ">5.0</td>
            <td style="line-height: 19px; ">1.4</td>
            <td style="line-height: 19px; ">3.1</td>
            <td style="line-height: 19px; ">3.1</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Solaris / JDK 1.3 (no JIT)</td>
            <td style="line-height: 19px; ">2.0</td>
            <td style="line-height: 19px; ">1.8</td>
            <td style="line-height: 19px; ">1.8</td>
            <td style="line-height: 19px; ">1.0</td>
            <td style="line-height: 19px; ">1.2</td>
            <td style="line-height: 19px; ">1.1</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Solaris / JDK 1.3 -client</td>
            <td style="line-height: 19px; ">19.8</td>
            <td style="line-height: 19px; ">1.5</td>
            <td style="line-height: 19px; ">1.1</td>
            <td style="line-height: 19px; ">1.3</td>
            <td style="line-height: 19px; ">2.1</td>
            <td style="line-height: 19px; ">1.7</td>
        </tr>
        <tr>
            <td style="line-height: 19px; ">Solaris / JDK 1.3 -server</td>
            <td style="line-height: 19px; ">1.8</td>
            <td style="line-height: 19px; ">2.3</td>
            <td style="line-height: 19px; ">53.0</td>
            <td style="line-height: 19px; ">1.3</td>
            <td style="line-height: 19px; ">4.2</td>
            <td style="line-height: 19px; ">3.2</td>
        </tr>
    </tbody>
</table>
<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="N10222"><strong>清单 1. 基准测试中用到的简单方法</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; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> staticEmpty() {  }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">void</span><span style="color: #000000; "> empty() {  }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> Object fetch() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> field; }<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> Object singleton() {<br />
</span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (singletonField </span><span style="color: #000000; ">==</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)<br />
singletonField </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> Object();<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> singletonField;<br />
}<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> Object hashmapGet() {<br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> hashMap.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">this</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
}<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; "> Object create() { <br />
</span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">new</span><span style="color: #000000; "> Object();<br />
}</span></div>
</strong></p>
</span><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; ">这些小基准测试也阐明了存在动态编译器的情况下解释性能结果所面临的挑战。对于 1.3 JDK 在有和没有 JIT 时，数字上的巨大差异需要给出一些解释。对那些非常简单的方法（ <code>empty</code> 和 <code>fetch</code> ），基准测试的本质（它只是执行一个几乎什么也不做的紧凑的循环）使得 JIT 可以动态地编译整个循环，把运行时间压缩到几乎没有的地步。但在一个实际的程序中，JIT 能否这样做就要取决于很多因素了，所以，无 JIT 的计时数据可能在做公平对比时更有用一些。在任何情况下，对于更充实的方法（ <code>create</code> 和 <code>hashmapGet</code> ），JIT 就不能象对更简单些的方法那样使非同步的情况得到巨大的改进。另外，从数据中看不出 JVM 是否能够对测试的重要部分进行优化。同样，在可比较的 IBM 和 Sun JDK 之间的差异反映了 IBM Java SDK 可以更大程度地优化非同步的循环，而不是同步版本代价更高。这在纯计时数据中可以明显地看出（这里不提供）。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">从这些数字中我们可以得出以下结论：对非争用同步而言，虽然存在性能损失，但在运行许多不是特别微小的方法时，损失可以降到一个合理的水平；大多数情况下损失大概在 10% 到 200% 之间（这是一个相对较小的数目）。所以，虽然同步每个方法是不明智的（这也会增加死锁的可能性），但我们也不需要这么害怕同步。这里使用的简单测试是说明一个无争用同步的代价要比创建一个对象或查找一个<code>HashMap</code> 的代价小。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">由于早期的书籍和文章暗示了无争用同步要付出巨大的性能代价，许多程序员就竭尽全力避免同步。这种恐惧导致了许多有问题的技术出现，比如说 double-checked locking（DCL）。许多关于 Java 编程的书和文章都推荐 DCL，它看上去真是避免不必要的同步的一种聪明的方法，但实际上它根本没有用，应该避免使用它。DCL 无效的原因很复杂，已超出了本文讨论的范围（要深入了解，请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-threads/#resources" style="color: #5c81a7; ">参考资料</a>里的链接）。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-threads/#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="4"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">不要争用</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">假设同步使用正确，若线程真正参与争用加锁，您也能感受到同步对实际性能的影响。并且无争用同步和争用同步间的性能损失差别很大；一个简单的测试程序指出争用同步比无争用同步慢 50 倍。把这一事实和我们上面抽取的观察数据结合在一起，可以看出使用一个争用同步的代价至少相当于创建 50 个对象。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">所以，在调试应用程序中同步的使用时，我们应该努力减少实际争用的数目，而根本不是简单地试图避免使用同步。这个系列的第 2 部分将把重点放在减少争用的技术上，包括减小锁的粒度、减小同步块的大小以及减小线程间共享数据的数量。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-threads/#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="5"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">什么时候需要同步？</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">要使您的程序线程安全，首先必须确定哪些数据将在线程间共享。如果正在写的数据以后可能被另一个线程读到，或者正在读的数据可能已经被另一个线程写过了，那么这些数据就是共享数据，必须进行同步存取。有些程序员可能会惊讶地发现，这些规则在简单地检查一个共享引用是否非空的时候也用得上。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">许多人会发现这些定义惊人地严格。有一种普遍的观点是，如果只是要读一个对象的字段，不需要请求加锁，尤其是在 JLS 保证了 32 位读操作的原子性的情况下，它更是如此。但不幸的是，这个观点是错误的。除非所指的字段被声明为 <code>volatile</code> ，否则 JMM 不会要求下面的平台提供处理器间的缓存一致性和顺序连贯性，所以很有可能，在某些平台上，没有同步就会读到陈旧的数据。有关更详细的信息，请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-threads/#resources" style="color: #5c81a7; ">参考资料</a>。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">在确定了要共享的数据之后，还要确定要如何保护那些数据。在简单情况下，只需把它们声明为 <code>volatile</code> 即可保护数据字段；在其它情况下，必须在读或写共享数据前请求加锁，一个很好的经验是明确指出使用什么锁来保护给定的字段或对象，并在你的代码里把它记录下来。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">还有一点值得注意的是，简单地同步存取器方法（或声明下层的字段为 <code>volatile</code> ）可能并不足以保护一个共享字段。可以考虑下面的示例：</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; "><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />
</span><span style="color: #0000FF; ">private</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> foo;<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; "> getFoo() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> foo; } <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; "> setFoo(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> f) { foo </span><span style="color: #000000; ">=</span><span style="color: #000000; "> f; }</span></div>
<p>如果一个调用者想要增加 <code>foo</code> 属性值，以下完成该功能的代码就不是线程安全的：</p>
<p>
</p>
<div style="background-color: #eeeeee; font-size: 13px; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; "> <img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />
setFoo(getFoo() </span><span style="color: #000000; ">+</span><span style="color: #000000; "> </span><span style="color: #000000; ">1</span><span style="color: #000000; ">);</span></div>
<p>
</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">如果两个线程试图同时增加&#160;<code>foo</code>&#160;属性值，结果可能是&#160;<code>foo</code>&#160;的值增加了 1 或 2，这由计时决定。调用者将需要同步一个锁，才能防止这种争用情况；一个好方法是在 JavaDoc 类中指定同步哪个锁，这样类的调用者就不需要自己猜了。</p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">以上情况是一个很好的示例，说明我们应该注意多层次粒度的数据完整性；同步存取器方法确保调用者能够存取到一致的和最近版本的属性值，但如果希望属性的将来值与当前值一致，或多个属性间相互一致，我们就必须同步复合操作 ― 可能是在一个粗粒度的锁上。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-threads/#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="6"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">如果情况不确定，考虑使用同步包装</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">有时，在写一个类的时候，我们并不知道它是否要用在一个共享环境里。我们希望我们的类是线程安全的，但我们又不希望给一个总是在单线程环境内使用的类加上同步的负担，而且我们可能也不知道使用这个类时合适的锁粒度是多大。幸运的是，通过提供同步包装，我们可以同时达到以上两个目的。Collections 类就是这种技术的一个很好的示例；它们是非同步的，但在框架中定义的每个接口都有一个同步包装（例如，&#160;<code>Collections.synchronizedMap()</code>&#160;），它用一个同步的版本来包装每个方法。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td style="line-height: 19px; "><img width="100%" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" alt="" /><br />
            <img alt="" width="8" height="6" border="0" src="http://www.ibm.com/i/c.gif" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td style="line-height: 19px; "><img width="100%" height="4" src="http://www.ibm.com/i/c.gif" alt="" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle" style="line-height: 19px; "><img width="16" src="http://www.ibm.com/i/v14/icons/u_bold.gif" height="16" border="0" alt="" /><br />
                        </td>
                        <td valign="top" align="right" style="line-height: 19px; "><a href="http://www.ibm.com/developerworks/cn/java/j-threads/#main" class="fbox" style="text-decoration: none; color: #5c81a7; font-family: verdana, nsimSun, arial, sans-serif; font-size: 12px; line-height: 13px; "><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="7"><span class="atitle" style="font-family: Arial, sans-serif; font-weight: bold; font-size: 18px; ">结论</span></a></p>
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; ">虽然 JLS 给了我们可以使我们的程序线程安全的工具，但线程安全也不是天上掉下来的馅饼。使用同步会蒙受性能损失，而同步使用不当又会使我们承担数据混乱、结果不一致或死锁的风险。幸运的是，在过去的几年内 JVM 有了很大的改进，大大减少了与正确使用同步相关的性能损失。通过仔细分析在线程间如何共享数据，适当地同步对共享数据的操作，可以使得您的程序既是线程安全的，又不会承受过多的性能负担。</p>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="resources"><span 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 全球站点上的&#160;<a href="http://www.ibm.com/developerworks/library/j-threads1.html?S_TACT=105AGX52&amp;S_CMP=cn-a-j" style="color: #5c81a7; ">英文原文</a>.&#160;<br />
    <br />
    </li>
    <li>请点击文章顶部或底部的&#160;<strong>讨论</strong>进入由 Brian Goetz 主持的，关于&#8220;Java 线程：技巧、窍门和技术&#8221;的&#160;<a href="http://www.ibm.com/developerworks/java_df.nsf/AllViewTemplate?S_TACT=105AGX52&amp;S_CMP=cn-a-j&amp;RestrictToCategory=23&amp;OpenForm" style="color: #5c81a7; ">讨论论坛</a>。&#160;<br />
    <br />
    <br />
    </li>
    <li>Jack Shirazi 编写的&#160;<a href="http://www.amazon.com/exec/obidos/ASIN/0596000154/none0b69" style="color: #5c81a7; "><em>Java Performance Tuning</em>&#160;</a>（O'Reilly &amp; Associates, 2000）可以为在 Java 平台上解决性能问题提供指导。本书引用的与本书一起提供的参考资料提供了很好的&#160;<a href="http://www.javaperformancetuning.com/tips.html" style="color: #5c81a7; ">性能调试技巧</a>。&#160;<br />
    <br />
    <br />
    </li>
    <li>Dov Bulka 的&#160;<a href="http://www.amazon.com/exec/obidos/ASIN/0201704293/none0b69" style="color: #5c81a7; "><em>Java Performance and Scalability，第 1 卷：Server-Side Programming Techniques</em>&#160;</a>（Addison-Wesley，2000）提供了大量的设计技巧和诀窍，可帮助您增强自己的应用程序的性能。&#160;<br />
    <br />
    <br />
    </li>
    <li>Steve Wilson 和 Jeff Kesselman 的&#160;<a href="http://www.amazon.com/exec/obidos/ASIN/0201709694/none0b69" style="color: #5c81a7; "><em>Java Platform Performance: Strategies and Tactics</em>&#160;</a>（Addison-Wesley，2000）为有经验的 Java 程序员提供了生成快速、有效的 Java 代码的技术。&#160;<br />
    <br />
    <br />
    </li>
    <li>Brian Goetz 最近的著作&#8220;&#160;<a href="http://www.javaworld.com/jw-02-2001/jw-0209-double.html" style="color: #5c81a7; ">Double-checked locking: Clever, but broken</a>&#8221;（JavaWorld，2001 年 2 月）详细探索了 JMM 并描述了特定情况下不使用同步的惊人后果。&#160;<br />
    <br />
    <br />
    </li>
    <li>公认的多线程权威 Allen Holub 在他的文章&#8220;&#160;<a href="http://www.javaworld.com/jw-02-2001/jw-0209-toolbox.html" style="color: #5c81a7; ">警告：多处理器世界中的线程</a>&#8221;（JavaWorld，2001 年 2 月）中揭示了为什么用于减少同步负担的大多数技巧都不起作用。&#160;<br />
    <br />
    <br />
    </li>
    <li>Peter Haggar 描述了怎样&#160;<a href="http://www.ibm.com/developerworks/cn/java/praxis/pr52.html" style="color: #5c81a7; ">用固定的，循环的顺序获取多个锁定以避免死锁</a>（developerWorks，2000 年 9 月）。&#160;<br />
    <br />
    <br />
    </li>
    <li>在他的文章&#8220;&#160;<a href="http://www.ibm.com/developerworks/cn/java/j-thread/index.html" style="color: #5c81a7; ">编写多线程 Java 应用</a>&#8221;（developerWorks，2001 年 9 月）中，Alex Roetter 介绍了 Java Thread API，概括了多线程涉及的问题，并为一般性的问题提供了解决方案。&#160;<br />
    <br />
    <br />
    </li>
    <li>Doug Lea 的&#160;<a href="http://www.amazon.com/exec/obidos/ASIN/0201310090/none0b69" style="color: #5c81a7; "><em>Concurrent Programming in Java，第 2 版</em>&#160;</a>（Addison-Wesley，1999）是关于 Java 语言中多线程编程的敏感问题的权威书籍。&#160;<br />
    <br />
    <br />
    </li>
    <li>&#8220;&#160;<a href="http://gee.cs.oswego.edu/dl/cpj/jmm.html" style="color: #5c81a7; ">同步和 Java 内存模型</a>&#8221;摘录自 Doug Lea 的关于&#160;<code>synchronized</code>&#160;的实际意义的著作。&#160;<br />
    <br />
    <br />
    </li>
    <li>Bill Pugh 的&#160;<a href="http://www.cs.umd.edu/~pugh/java/memoryModel" style="color: #5c81a7; ">Java 内存模型</a>为您学习 JMM 提供了一个很好的起点。&#160;<br />
    <br />
    <br />
    </li>
    <li><a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html" style="color: #5c81a7; ">&#8220;Double Checked Locking is Broken&#8221;</a>声明描述了为什么 DCL 在用 Java 语言实现时没有用。&#160;<br />
    <br />
    <br />
    </li>
    <li>Bill Joy、Guy Steele 和 James Gosling 的&#160;<a href="http://www.amazon.com/exec/obidos/ASIN/0201310082/none0b69" style="color: #5c81a7; "><em>The Java Language Specification，第 2 版</em>&#160;</a>（Addison-Wesley，2000）的第 17 章描述了 Java 内存模型的深层细节问题。&#160;<br />
    <br />
    <br />
    </li>
    <li>IBM T.J. Watson 研究中心有一整个项目组投入到&#160;<a href="http://www.research.ibm.com/PM/perf_mgt.html" style="color: #5c81a7; ">性能管理</a>中。&#160;<br />
    <br />
    <br />
    </li>
    <li>请在&#160;<a href="http://www.ibm.com/developerworks/cn/java/" style="color: #5c81a7; ">developerWorks Java 技术专区</a>查找更多的参考资料。&#160;<br />
    </li>
</ul>
<br />
<br />
<p style="padding-bottom: 8px; padding-top: 5px; margin-top: 0px; margin-bottom: 0px; "><a name="author"><span 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; ">Brian Goetz 是一名软件顾问，并且过去 15 年来一直是专业的软件开发人员。他是&#160;<a href="http://www.quiotix.com/" style="color: #5c81a7; ">Quiotix</a>，一家坐落在 Los Altos，California 的软件开发和咨询公司的首席顾问。请通过&#160;<a href="mailto:brian@quiotix.com?cc=" style="color: #5c81a7; ">brian@quiotix.com</a>&#160;与 Brian 联系。</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
</span>
<img src ="http://www.blogjava.net/rainmanyang/aggbug/231868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rainmanyang/" target="_blank">rainman</a> 2008-09-30 13:31 <a href="http://www.blogjava.net/rainmanyang/articles/231868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>